CZ_HELP!▀INDEX=TUTORIAL_INDEXB@LICENSE_AGREEMENçtLICENSE_A{PRODUCT_SUPPORTÇ>STARTING_CZ├▓USING_CZz!.USING_CZ_A¡'+USING_CZ_B▌,YABOUT_CZ;1áIS_BULLETα6ÉIS_A_DATABASEu9╧IS_DBFI=ºIS_A_BTREE⌡AIS_A_NETWORK²F6IS_FILE_LOCKING8L≈IS_NLS4QDESIGN_A_DBTSICREATE_A_DBóX\ADD_TO_THE_DB^7QUERY_THE_DB?b}UPDATE_THE_DB┴fíDELETE_A_RECORDgkBC_COMPILE_WITHo=LIB_WITH┴rëMAKE_A_QLBOv╗LINK_WITH{zCALL_BULLETÄ~òSPECS_OVERALL(äNSPECS_DBF{êpSPECS_DBF_A≡ìäSPECS_DBF_ByÆSPECS_DBF_CÆû7SPECS_INDEX╬Ö⌐SPECS_INDEX_A|₧.SPECS_INDEX_B»óZSPECS_INDEX_CºSPECS_MEMORY0¼nSPECS_MEMORY_Aú▒VSPECS_OS_CALLS■╢<SPECS_LANGUAGES?╗/SPECS_OSESs╛MSPECS_NETWORKS┼├HSPECS_PERFORMANC╔ùSPECS_PERF_A«═ÇSPECS_PERF_B3╥'SPECS_PERF_C_╫åSPECS_PERF_DΩ█NINITXB=αEXITXB┴πíATEXITXBgΦMEMORYXB}φ┼BREAKXBG≤BACKUPFILEXBb°GSTATHANDLEXB«√╜GETEXTERRORXBp²6DVMONCXB½;CREATEDXBδ,OPENDXBwCLOSEDXBÿñSTATDXBA₧READDHXBΣ╟FLUSHDHXB░ìCOPYDHXBB⌡ZAPDHXB< ¬CREATEKXBδ"CREATEKXB_A ']CREATEKXB_Ba,∩CREATEKXB_CU2CREATEKXB_Dj7XCREATEKXB_E╟<eCREATEKXB_F1B£OPENKXB╥GΣCLOSEKXB╗IÆSTATKXBRLvREADKHXB═N╝FLUSHKHXBÄSrCOPYKHXBYΦZAPKHXB≥ZòGETDESCRIPTORXBî]╚GETRECORDXBYalADDRECORDXB╩d4UPDATERECORDXBiXDELETERECORDXB`m/UNDELETERECORDXBörZPACKRECORDSXB≤vçFIRSTKEYXB{MEQUALKEYXB╤ÇNEXTKEYXB≥àMPREVKEYXBDèRLASTKEYXB¢ÄnSTOREKEYXBÆ DELETEKEYXB3û╚BUILDKEYXBÖ\CURRENTKEYXBa£XGETFIRSTXB╛₧⌐GETEQUALXBlóGETNEXTXBwº╗GETPREVXB7½╝GETLASTXB°«NINSERTXBK▓UPDATEXB\╖~REINDEXXB▀╝▐LOCKXB┬┬╞UNLOCKXBì╚ÆLOCKKEYXB$╬9UNLOCKKEYXBb╙)LOCKDATAXBÉ╪oUNLOCKDATAXB▐~DRIVEREMOTEXBçπ¿FILEREMOTEXB4τ½SETRETRIESXBΣδùDELETEFILEDOSÇ∩πRENAMEFILEDOSh≡²CREATEFILEDOSj≤ACCESSFILEDOSq÷+OPENFILEDOSí°;SEEKFILEDOSßⁿgREADFILEDOSM▀EXPANDFILEDOS1âWRITEFILEDOS╣	VCLOSEFILEDOSMAKEDIRDOS ┐ACCESSPACKΣ?BREAKPACK(9COPYPACKf╩CREATEDATAPACK5PCREATEKEYPACKèDESCRIPTORPACKÉ"+DOSFILEPACK└&~DVMONPACKC+3EXITPACK{/FIELDDESCTYPEê0HANDLEPACKò4∩INITPACKë6⌡MEMORYPACKâ8&OPENPACK«9█REMOTEPACKÄ< SETRETRIESPACKÆ>+STATDATAPACK┬AWSTATKEYPACKE╬STATHANDLEPACK±HÇXERRORPACKvJàERRORS_BULLETLæERRORS_BULLET_BûO┌ERRORS_BULLET_CuRRERRORS_BULLET_D╠UNERRORS_BASICZERRORS_BASIC_B)_├ERRORS_DOS±b▓ERRORS_DOS_B¿g~ERRORS_DOS_C+m╬INITXBSRC■q*EXITXBSRC-tATEXITXBSRCFu╧MEMORYXBSRCxüBREAKXBSRCá{óBACKUPFILEXBSRCG~HSTATHANDLEXBSRCöüGETEXTERRORXBSRCäεDVMONCXBSRCçσCREATEDXBSRC⌡ëƒCREATEDXBSRC_AÖÄ{OPENDXBSRCô▌CLOSEDXBSRC√ò7STATDXBSRC7ùvREADDHXBSRC▓ÜÜFLUSHDHXBSRCQ£√COPYDHXBSRCQ₧PZAPDHXBSRCªáuCREATEKXBSRC ó╟CREATEKXBSRC_A∞ñìOPENKXBSRC~⌐{CLOSEKXBSRC■¼7STATKXBSRC:«╩READKHXBSRC	│ÖFLUSHKHXBSRCº┤·COPYKHXBSRCª╢PZAPKHXBSRC√╕tGETDESCRIPTORXBSt║GETRECORDXBSRCî╛òADDRECORDXBSRC&┬∞UPDATERECORDXBSR╞gDELETERECORDXBSRâ╦╠UNDELETERECORDSRT═╠PACKRECORDSXBSRC%╧?FIRSTKEYXBSRCi╨8EQUALKEYXBSRCª╘'NEXTKEYXBSRC╥╪»PREVKEYXBSRCå█LASTKEYXBSRCÉ▐ªSTOREKEYXBSRC;ßⁿDELETEKEYXBSRC<σⁿBUILDKEYXBSRC=ΘƒCURRENTKEYXBSRCß∞xGETFIRSTXBSRC^∩▓GETEQUALXBSRC≤ïGETNEXTXBSRCÑ≈▄GETPREVXBSRCå√πGETLASTXBSRCn ¡INSERTXBSRC ╒UPDATEXBSRC·ÅREINDEXXBSRCÄôLOCKXBSRC&DUNLOCKXBSRCoLOCKKEYXBSRCçîUNLOCKKEYXBSRCLOCKDATAXBSRC"├UNLOCKDATAXBSRCµ#√DRIVEREMOTEXBSRCµ&"FILEREMOTEXBSRC
)SETRETRIESXBSRC+╒DELETEFILEDOSSRC⌠,àRENAMEFILEDOSSRC~.	CREATEFILEDOSSRCî0┼ACCESSFILEDOSSRCV2└OPENFILEDOSSRC4∩SEEKFILEDOSSRC6READFILEDOSSRC)8EXPANDFILEDOSSRC.;mWRITEFILEDOSSRCá<┬CLOSEFILEDOSSRCg?MAKEDIRDOSSRCâ@é~INDEX            CZ.HLP-BULLET for QB/BASIC PDS

┌─── System ───┐┌──────────────── Mid-level Record/Key Access ────────────────┐
│InitXB        ││CreateDXB    CreateKXB    GetDescriptorXB     FirstKeyXB     │
│ExitXB        ││OpenDXB      OpenKXB      GetRecordXB         EqualKeyXB     │
│AtExitXB      ││CloseDXB     CloseKXB     AddRecordXB         NextKeyXB      │
│MemoryXB      ││StatDXB      StatKXB      UpdateRecordXB      PrevKeyXB      │
│BreakXB       ││ReadDHXB     ReadKHXB     DeleteRecordXB      LastKeyXB      │
│BackupFileXB  ││FlushDHXB    FlushKHXB    UndeleteRecordXB    StoreKeyXB     │
│StatHandleXB  ││CopyDHXB     CopyKHXB     PackRecordsXB       DeleteKeyXB    │
│GetExtErrorXB ││ZapDHXB      ZapKHXB                          BuildKeyXB     │
│DVmonCXB      ││                                              CurrentKeyXB   │
└──────────────┘└─────────────────────────────────────────────────────────────┘
┌──── High-level Access ────┐┌─────────────────── Network ────────────────────┐
│GetFirstXB    InsertXB     ││LockXB           UnlockXB        LockKeyXB      │
│GetEqualXB    UpdateXB     ││UnlockKeyXB      LockDataXB      UnlockDataXB   │
│GetNextXB     ReindexXB    ││DriveRemoteXB    FileRemoteXB    SetRetriesXB   │
│GetPrevXB                  │└────────────────────────────────────────────────┘
│GetLastXB                  │┌───────────── Low-level DOS Access ─────────────┐
│                           ││DeleteFileDOS    OpenFileDOS     WriteFileDOS   │
│                           ││RenameFileDOS    SeekFileDOS     CloseFileDOS   │
│ Move cursor to index item ││CreateFileDOS    ReadFileDOS     MakeDirDOS     │
│ and press <Enter>.        ││AccessFileDOS    ExpandFileDOS                  │
└───────────────────────────┘└────────────────────────────────────────────────┘
See: TUTORIAL_INDEX

~TUTORIAL_INDEX   CZ.HLP-BULLET for QB/BASIC PDS

┌─── CZ.COM ───┐┌─────────────────────── Using BULLET ───────────────── 1.02 ─┐
│Starting_CZ   ││What:              How to:                                   │
│Using_CZ      ││ is_BULLET          design_a_DB         BC_compile_with      │
│About_CZ      ││ is_a_database      create_a_DB         LINK_with            │
│              ││ is_DBF             add_to_the_DB       LIB_with             │
└──────────────┘│ is_a_Btree         query_the_DB        make_a_QLB           │
┌─ Error Codes─┐│ is_a_network       update_the_DB      ┌─────────────┐       │
│Errors_BULLET ││ is_file_locking    delete_a_record    │ call_BULLET │       │
│Errors_BASIC  ││ is_NLS                                └─────────────┘       │
│Errors_DOS    ││                    LICENSE_AGREEMENT   Product_Support      │
└──────────────┘└─────────────────────────────────────────────────────────────┘
┌───────────── Structure Pack Types ──────────────┐ ┌───── Specifications ────┐
│AccessPack       DVmonPack        RemotePack     │ │Specs_Overall            │
│BreakPack        ExitPack         SetRetriesPack │ │Specs_DBF                │
│CopyPack         FieldDescTYPE    StatDataPack   │ │Specs_Index              │
│CreateDataPack   HandlePack       StatKeyPack    │ │Specs_Memory             │
│CreateKeyPack    InitPack         StatHandlePack │ │Specs_OS_Calls           │
│DescriptorPack   MemoryPack       XErrorPack     │ │Specs_Languages          │
│DOSFilePack      OpenPack                        │ │Specs_OSes               │
│                                                 │ │Specs_Networks           │
│                                                 │ │Specs_Performance        │
└─────────────────────────────────────────────────┘ └─────────────────────────┘
See: License_Agreement

~License_Agreement

Before using this software you must agree to the following:

 1. You are not allowed to operate more than one (1) copy of this software
    package at one time per license. This means that if you have 10 programmers
    that COULD possibly use the BULLET library at the same time, you must also
    have ten (10) BULLET licenses.

 2. You are not allowed to distribute non-executable code containing BULLET
    code. This means that you are not allowed to redistribute BULLET code as
    another .LIB, for example. Also, if BULLET code is to be contained in a
    Dynamic Link Library (DLL) then it must be part of a stand-alone product.
    This means that you cannot provide a .DLL containing BULLET code if that
    .DLL is to be used as a programming library for other programmers. If you
    wish to distribute non-executable code containing BULLET code you must
    obtain written permission from the author.

 3. This license grants you the right to use the BULLET library code on a
    royalty-free basis.




See: License_a                                                          -MORE-

~License_a

 4. BULLET is owned by the author, Cornel Huth, and is protected by United
    States copyright laws and international treaty provisions. You are not
    allowed to make copies of this software except for archival purposes.

 5. You may not rent or lease BULLET. You may not transfer this license without
    the written permission of the author. If this software is an update or
    upgrade, you may not sell or give away previous versions.

 6. You may not reverse engineer, decompile, or disassemble this software.

 7. There are no expressed or implied warranties with this software.

 8. All liabilities in the use of this software rest with the user.

 9. U.S. Government Restricted Rights. This software is provided with
    restricted rights. Use, duplication, or disclosure by the Government is
    subject to restrictions as set forth in subparagraph (c)(1)(ii) of the
    Rights in Technical Data and Computer Software clause at 52.227-7013.
    Manufacturer is Cornel Huth/6402 Ingram Rd/San Antonio, TX 78238.

    This agreement is governed by the laws of the state of Texas.

See: Product_Support

~Product_Support

Phone support is available 7 days a week, 9AM-9PM Central Time (USA) for
registered users. Phone support for non-registered users is not available.

If you are a registered BULLET user you may call 1-512-684-8065 during the
hours listed above. If I'm not available to handle your call leave a message
on the machine briefly explaining the basis for your call. You MUST leave a
message stating that you want the return call to be COLLECT. Calls cannot be
returned unless you state this in your message (no exceptions).

If you have a problem that you believe to be due to a bug in BULLET, mail a
diskette with the program and data to recreate the problem. Include all source
code, data files, libraries, etc. on the disk that is needed to recreate the
problem. Do not send your entire application. Do not send only a listing. Send
only the code that is sufficient for another person to recreate the problem.
(No exceptions.)

A support BBS is not currently available but is expected to be operational in
the near future. If you would like to use a BBS to communicate you may reach me
at NUL BBS, 1-512-615-6852 (2400-), 615-6851 and 6853 (9600+, HST, V.32bis) or
you may contact me on FidoNet at Cornel Huth @ 1:387/800 or through Internet at
Cornel.Huth@p8.f800.n387.z1.fidonet.org.

See: Starting_CZ

~Starting_CZ
┌─────────────────────────────────────────────────────────────────────────────┐
│At CZ's initial load it looks into the current directory for CZ.HLP, then in │
│the directory of CZ.COM, and last it looks for the pathname specified by the │
│DOS variable CZH (SET CZH=C:\DOC\CZ.HLP). Use /f: for alternate locations.   │
└─────────────────────────────────────────────────────────────────────────────┘
Load CZ.COM from the DOS command line. Options are:

        /f:helpfile.ext       Use other than default CZ.HLP help file
        /h# where n=1 to 4    Use alternate hot-key from default Alt-F1
            #=1  Ctrl-h
            #=2  F12
            #=3  left+right Shift
            #=4  Alt-left Shift
        /u  uninstall CZ from memory (after initial load)
        /s  temporarily put CZ to sleep by restoring all hooked vectors
        /r  restore CZ from its sleep by rehooking vectors
        /?  quick help

 E.g., C>cz /f:D:\PRG_C\CBULLET.HLP   supply entire pathname when using /f:
       C>cz /h1                       change hot key from Alt-F1 to Ctrl-H

You can use the /f: at any time, even after the initial install, as all but /u.

See: Using_CZ

~Using_CZ

To activate CZ press the hot-key while the cursor is on the word you want to
look up. Information on that word, if any, is displayed. If none is available,
an index of help items in the dictionary is shown. Along the top-right of the
index screens is the control bar. You can quickly move to the control bar by
pressing <Home> or the See: line with <End>. Move the cursor to TUTORIAL_INDEX
to select the second index screen or QUIT to return to whatever you were doing
or over any index item. Then press <Enter> to move to that item's help entry.
<F1> can be used as <Enter>. A mouse can also be used and is recommended.

For example, to find out about CreateDXB, type it in your application and move
the cursor on it. Press the hot-key. The CreateDXB screen is displayed. To see
what pack type it uses, move to CreateDataPack (at Pack:) and press <Enter>.
For a source example, move the cursor to Src: CreateDXBsrc. To go straight to
the index from your application, press the hot-key with the cursor on a blank
space. The <Esc> key returns you to your application.

┌─────────────────────────────────────────────────────────────────────────────┐
│If there are more screens for the current topic the See: line has the same   │
│topic name plus a letter, and -MORE- at the end. Move the cursor (or mouse)  │
│to the topicname text (by using the <End> key) and press <Enter> (or click). │
└─────────────────────────────────────────────────────────────────────────────┘

See: Using_CZ_a                                                         -MORE-

~Using_CZ_a

┌─────────────────────────────────────────────────────────────────────────────┐
│CZ.COM can be loaded high but it is ESSENTIAL that you have at least 15K of  │
│free UMB RAM available. It will load in as little as 4.5K but it will not    │
│operate correctly. Use MEM/c to see how much Upper Memory RAM is available.  │
└─────────────────────────────────────────────────────────────────────────────┘
CZ opens the help file at installation. The help file is opened for Read-Only
access with a Deny None sharing attribute. The file is closed when CZ is
uninstalled (C>cz /u). CZ makes use of its own 256-byte stack.

If you have several CZ help files, rename them to their particular application.
For example:

Rename the QuickBASIC BULLET CZ.HLP to QBULLET.HLP. Put QBULLET.HLP into your
help files directory. Put SET CZH=C:\HELPFILES\QBULLET.HLP in your AUTOEXEC.BAT
file. The next time CZ is installed it uses QBULLET.HLP from C:\HELPFILES.

At anytime you can specify CZ.COM to use another help file. For example, if the
current CZ help file is QBULLET.HLP but you want to use the CBULLET.HLP file,
use C>cz /f:\helpfiles\cbullet.hlp. CBULLET.HLP then becomes the active file
the next time you popup CZ.


See: Using_CZ_b                                                         -MORE-

~Using_CZ_b

Limitations:

1) The QB environment may interpret the Alt-F1 keypress after CZ has popped
down. If this is a problem then change the hotkey, e.g., C>cz /h2 to use F12.

2) In the QB (and QBX) environment the keypad <Enter> key is not recognized by
CZ. Use the main <Enter>. This occurs only in then QB 4.5 and QBX editors.

3) If, after returning from CZ, the QB environment's menu bar does not respond
to Alt-keys (like an Alt-F), click the left mouse button a few times, or press
F6 to go to the immediate window and execute a SHELL. It's unlikely that you'll
encounter this.

4) CZ is a stable TSR but like all TSRs in a DOS system, unforseen events can
take place that could conceivably cause the computer to crash. Therefore, it's
recommended that you save your work often (you should do so whether a TSR is
installed or not).

5) CZ currently doesn't reset the mouse. If you're having mouse trouble, you'll
need to reset your mouse driver (type MOUSE at the C>). Many problems come from
other programs not restoring the mouse cursor to its original state.

See: About_CZ

~About_CZ

              ┌─────────────────────────────────────────────────┐
              │                     CZ HELP                     │
              │                                                 │
              │      Context-sensitive Online Help Manager      │
              │                                                 │
              │                     for the                     │
              │                                                 │
              │               █▀█ █ █ █  █  █▀ ▀█▀              │
              │               ██ █ █ █  █  █  █               │
              │               █ █ █ █ █  █  █   █               │
              │               █▄█ █▄█ █▄ █▄ █▄  █               │
              │                                                 │
              │                compiler libraries               │
              │                                                 │
              │       MS-DOS QuickBASIC/BASIC PDS version       │
              │        (available for most DOS compilers)       │
              │                                                 │
              │           Copyright 1992  Cornel Huth           │
              │                                                 │
              │Ver 1.02         INT2F MuxID=C2         15-Aug-92│
              └─────────────────────────────────────────────────┘

See: Specs_Overall

~is_BULLET - What?

BULLET is a program module that handles the details of putting information to
and getting information from your hard disk using a standard data file format
called the Xbase DBF format with very fast and efficient index file routines.
It can be used as-is by most DOS compilers.

BULLET is written in 100% assembly language. Why? Two reasons. First, control.
There's no compiler code or run-time library code in between BULLET and your
data. Second, efficiency. BULLET knows exactly what it requires from the
operating system and when. Result: fast, small, and robust applications.













See: is_a_database

~is_a_database - What?

A database is a collection of data arranged so that the it can be accessed as
useful information. For example, let's say we have two files. Each consists of
two fields. The first file has codenumber and score. The second file has a
codenumber and name. Separately, the files are merely a collection of data.
Together, however, they tie the name to the score:

        score codenumber   codenumber name
         99     100           100     John
         87     155           105     Paul
         66     125           110     George
          :      :             :       :

Codenumber 100 is John, who scored 99. The other members scores are not in the
abbreviated data file listing.

A database can be a single data file but more often it is a group of related
data files and usually these data files are indexed by keys (in the index file,
also called key file) so that very fast, direct access is possible.




See: is_DBF

~is_DBF - What?

DBF is the file extension of dBASE III-compatible data files (filename.DBF).
The file format is used by dBASE IV, FoxPro and many other database programs.
Many programs can also use the file format to import/export data using it.
The DBF format is the most common data file format used on PCs.

A DBF-compatible data file consists 3 distinct areas. First is the data header.
This contains information such as the number of records in the file. Second is
the field descriptors. These descriptors define the makeup of each field in the
record. The third is the record area. Each record is a logical unit of data.

For example, a record, all of which are made up of the same fields but with
different data, could (conceptually) look like this:

               field 1         field 2         field 3       field n
           ┌────────────────┬───────────────┬────────────
  record 1 │Johnson         │Larry          │465310555     ...
           ├────────────────┼───────────────┼────────────
  record 2 │Aberdeen        │Zara           │465230555     ...
           ├────────────────┼───────────────┼────────────
  record n


See: is_a_Btree  Specs_DBF

~is_a_Btree - What?

A b-tree is a sorting method ideally suited to data structures maintained on a
hard disk. It is very fast on retrieval and is inherently self-balancing during
inserts and deletes. Self-balancing ensures performance remains consistent.

The reason the b-tree is ideally suited to hard disks is that, when looking for
a particular key, most of the time involved in accessing the key is spent by
the hard drive moving to various locations on the disk. The task of a good
access method is to reduce the number of seeks that the disk must perform. The
b-tree accomplishes this by maintaining several keys (perhaps 50) on each node,
with the necessary pointers to previous and following nodes. A b-tree of order
20 (19 keys per node) can find a key in a file of 1,000,000 keys in a MAXIMUM
of 5 disk accesses, where each disk access visits a node.

'BASIC program to find *max* seeks needed/avg time
Keys& = 1000000: KeysPerNode = 19: AvgSR = 25
Order = KeysPerNode + 1
max = (LOG((Keys& + 1) / 2) / LOG(Order / 2))
PRINT "Max nodes accessed for"; Keys; "keys & b-tree of order"; Order;
PRINT "is"; max; "nodes"
PRINT "Max disk time based on avg seek+read of"; AvgSR;
PRINT "ms is"; AvgSR / 1000 * max; "seconds"

See: is_a_network  Specs_Index

~is_a_network - What?

A network is a group of computers able to communicate with one another. Often
called a LAN (local area network), a network allows resources to be shared.
Sharing resources can lead to problems if steps are not taken to ensure that
two computers don't try to share the same resource at the same time. For
example, say two computers try to change the same record in a file on a network
drive. Let's say both users are accessing the number of widgets in inventory.
The first user gets there a micro-second before the second and allocates the
last widget in stock. The second user comes in right after and, since the first
user has not yet updated the inventory, allocates the very same widget. One
widget, two users. When the first user updates the inventory, widgets in
inventory is changed to 0 (previous - 1). The second updates the inventory in
the same manner and sets widgets to 1 less what it was when it started, or 0
also. You see the problem.

In order to successfully share a file on a network, the file must first be
locked to a single user. Once that user has locked the file, he has sole access
to the data within it and he will not experience the scenario above. When the
user has completed the changes, he unlocks the file so that others may use it.




See: is_file_locking

~is_file_locking - What?

File locking is a means to obtain exclusive access to a file. This is needed in
cases of multiple programs or users accessing a shared file at the same time.

There are several methods to ensure only one process or user has access to a
file. The first method is to open the file so that while the file is open only
your program can access any part of it. This is simple to implement and the
operating system handles the details of this. However, it requires your program
to open/close files all the time since no other process may access the file
while it is open.

Another method is to use byte-level locks. Also managed by the OS, this method
allows for restricting access to any particular region within the file. Which
regions are to be locked is to be determined by your program, however, and it
can be complex to perform multiple locks at the byte, field, or record level.

Another use of the byte-level lock is to specify that all bytes within the file
are to be locked. This greatly simplifies the process of obtaining a lock and
has the advantage over a file access lock of not needing to open/close the file
for each lock. It is very fast and easy to implement. BULLET offers all three
lock types.


See: is_NLS

~is_NLS - What?

NLS stands for National Language Support. This feature is available in DOS 3.3
and later. BULLET makes use of NLS by getting from DOS the current DOS country
collate-sequence table. The collate table is used to properly sort mixed-case
character strings and also foreign (or non-USA) language character strings
according to that country's alphabet. This is an option but is recommended.

In addition, BULLET provides for a programmer-supplied collate-sequence table.















See: design_a_DB

~design_a_DB - How to

To design a database, above all else, know what information you require from
it. Having established what you need to know, collect the data that lets you
formulate this into useful information.

For example, you want to track a class of students and determine how well they
achieve on tests. The criterion you use is the test score. You determine that
your data is 1) students, 2) tests, and 3) test scores. Too simplify, you use
a single 20-character field for student, a 1-character field for test number
("1" to the "n" tests), and a numeric field for test scores (0 to 100).

Since the objective is to track students' scores, arrange the data so that
output consists of each student's score in test order. Do this by specifying an
index file containing an index based on the student's name and test number:

  KeyExpression$ = "STUDENT + TEST"       'combine two character fields

This is the reason that field 2, test number, is a character field. It can more
easily be combined with other character fields than a numeric field. By using
the routines of the database langauge, you can easily create the data and index
files, add data, list student's scores, or make changes to the database. Note:
these How_to examples are meant only to show the basis behind an operation.

See: create_a_DB  CreateKXB

~create_a_DB - How to

Having defined the database, create it. First, create the datafile based on the
3 fields you defined in your design. To do this, DIM an array for the field
descriptors for the number of fields (see also CreateDataPack):

DIM FD(1 TO 3) AS FieldDescTYPE
FD(1).FieldName = "STUDENT" + STRING$(10,0)   'must be zero-filled
FD(1).FieldType = "C"
FD(1).FieldLength = CHR$(20)               ┌─────────────────────────────────┐
FD(1).FieldDC = CHR$(0)                    │The FD() is a structure element  │
FD(2).FieldName = "TEST" + STRING$(10,0)   │in CDP (CreateDataPack) as in:   │
FD(2).FieldType = "C"                      │CDP.Func=CREATEDXB               │
FD(2).FieldLength = CHR$(1)                │  :        :                     │
FD(2).FieldDC = CHR$(0)                    │CDP.FieldListPtrOff=VARPTR(FD(1))│
FD(3).FieldName = "SCORE" + STRING$(10,0)  │CDP.FieldListPtrSeg=VARSEG(FD(1))│
FD(3).FieldType = "N"                      │  :        :                     │
FD(3).FieldLength = CHR$(3)                └─────────────────────────────────┘
FD(3).FieldDC = CHR$(0)

Call CreateDXB to create the data file. To create the index file, first open
the data file just created, then call CreateKXB to create the index file. Open
the index file so we can use it (data file is already open).

See: add_to_the_DB  CreateDXB

~add_to_the_DB - How to

Once you have the database designed and the data and key files created and open
you can start putting the student's test data into it. Note that the DBF-format
requires that all data in a data file be in ASCII format. This means that we
must convert the numeric test score into its ASCII form. BASIC has the STR$()
function to do this. In addition, numbers generally should be right-justified
in their field. BASIC has the RSET statement to do this:

TYPE StudentRecordTYPE 'this structure is exactly how the record is on disk
tag AS STRING * 1      'DBF delete tag used to identify deleted records
sname AS STRING * 20   '--the tag is always present in any DBF record
testn AS STRING * 1    '  don't forget to include it in all your TYPEs
score AS STRING * 3
END TYPE '25
DIM SR AS StudentRecordTYPE
     :
DO
   INPUT "Student name, test number, score:",n$,t$,s%
   SR.sname = n$ : SR.testn = t$ : RSET SR.score = STR$(s%)
   status = DoInsert(SR, keyhandle)
LOOP UNTIL LEN(n$) = 0 OR (status <> 0)


See: query_the_DB  InsertXB

~query_the_DB - How to

Now that you have data in the database you want to see what's in there. Since
the index file is in "STUDENT + TEST" order, the information we'll be getting
out of the database is in Student name order, with each student's scores in
test number order.

If we want to look at all the students, we can use GetFirstXB to retrieve the
first student's score for the first test. GetNextXB retrieves the next record
(the first student's score for the second test), and so on. When all records
have been retrieve GetNextXB returns an End Of File error code.

If we want to look at a particular student's score only, we can use GetEqualXB
to go directly to a student's first test score. GetNextXB get his next and so
on until GetNextXB retrieves the next student's first test score. You can stop
at this point (student names no longer match).

We might also want to find all students who scored less than 65 on any test. To
do this we can GetFirstXB, check SR.score for < 65 and if so print that record.
Continue by using GetNextXB, printing each record that has a score < 65.




See: update_the_DB  GetFirstXB

~update_the_DB - How to

To update a particular record in the database we must first locate and identify
it using one of the get routines such as GetEqualXB. The Get() routine return
the record data, and also the physical record number of the record accessed,
into the AccessPack RecNo. Having used one of the Get() routines to read the
data record from disk to memory, you can make any changes to the data record in
memory. E.g., if a student's score needs to be changed from a 69 to a 96, first
find the record (and its RecNo), then update the score field:

   INPUT "Student name, test number",n$,t$
   SR.sname = n$ : SR.testn = t$
   status = DoGetStudentTestScore(SR, keyhandle, Recno&)
   DO UNTIL status <> 200   'student as entered not found, call search routine
      status = DoFindStudentTestScore(SR, keyhandle, RecNo&)
   LOOP
   IF status = 0 THEN
      PRINT "Student:" ; SR.sname ; " old score:" ; SR.score
      INPUT "Enter new score:"; s% : RSET SR.score = STR$(val(s%))
      status = DoUpdateTestScore(SR, keyhandle, Recno&)
   ENDIF

Any change to a key field will initiate a key file update automatically.

See: delete_a_record  UpdateXB

~delete_a_record - How to

To delete a particular record in the database we must first locate it using
one of the get routines such as GetEqualXB. These Get() routines return the
actual record number of the data record accessed by Get() into the AccessPack
RecNo. Having used one of the Get() routines to find the data record, make a
call to the delete function:

   'delete all of Student's records, 1 record for each test he's taken
   INPUT "Student name to delete",n$
   SR.sname = n$
   status = DoGetStudent(SR, keyhandle, RecNo&)
   DO UNTIL status <> 200   'student as entered not found, call search routine
      status = DoFindStudent(SR, keyhandle,RecNo&)
   LOOP
   DO
      status = DoDeleteRecord(SR, RecNo&)  'does not affect key files
      IF status = 0 THEN status = GetNextChkIfSameStudent(SR,keyhandle,RecNo&)
   LOOP UNTIL status <> 0

The DeleteRecordXB routine does not physically remove the record from the data
file but instead tags it as being "deleted".


See: BC_compile_with  DeleteRecordXB

~BC_compile_with - How to

To create a stand-alone EXE file, compile your BASIC source as required. No
special compiler switches are required. When your compiler is finished and has
reported no errors during the compile, use the LINK program supplied with your
compiler to resolve any external references in your BASIC program code that are
dependant on the BULLET library code.

For example, if you have a single-module BASIC source file called STUGRADE.BAS,
compile it:

 C>bc STUGRADE /o;

If successful, use the LINK program to build the final EXE:

 C>link STUGRADE,STUGRADE.EXE,nul,BULLET.LIB;

If successful, link creates STUGRADE.EXE ready to be run at the DOS prompt.

The BASIC runtime is also supported by BULLET. Just compile as you normally
would (without the /o, of course).



See: LIB_with

~LIB_with - How to

BULLET.LIB is composed of many separate assembly language modules. All these
modules have been combined into a single, easy-to-use .LIB library. While it
is possible to combine, or add, other .OBJ files or even other .LIB files to
BULLET.LIB, I do NOT, -NOT-, recommend that you do so.

If you need to use two or more libraries with your programs, no problem, LINK
can handle as many as you have. When LINK prompts you for a library file, just
enter BULLET.LIB followed by any other library you need. For example:

 C>LINK

 Microsoft (R) Segmented-Executable Linker  Version 5.10
 Copyright (C) Microsoft Corp 1984-1990.  All rights reserved.

 Object Modules [.OBJ]: STUGRAD1+STUGRAD2+STUB
 Run File [STUGRAD1.EXE]: STUGRADE.EXE
 List File [NUL.MAP]: nul
 Libraries [.LIB]: BULLET MOUSE;

Consult your linker programs documentation for more.


See: make_a_QLB

~make_a_QLB - How to

If you use the QuickBASIC environment to program in, you need to build a .QLB
so that you can call the BULLET routines. While QuickBASIC can load only a
single QLB ( C>qb /L quicklib.qlb ), you can combine many LIB and OBJ modules
to create the QLB. For example, to make a QLB of the BULLET.LIB, MOUSE.LIB,
and STUB.OBJ modules:

 C>link /QU BULLET.LIB+MOUSE.LIB+STUB, IDE.QLB, nul, BQLB45.LIB;

Note the extension .LIB on the library modules. This is REQUIRED since LINK
assumes .OBJ by default, as in the case of STUB, above. The quick library,
BQLB45.LIB, is required for the Libraries: prompt. The exact name depends on
the compiler version. Consult your compiler documentation for more.

BASIC PDS note: BASIC PDS requires that all BASIC modules included into a QLB
be compiled using the /Fs compiler switch. This instructs the compiler to
generate code compatible with "far strings". Since BULLET is written entirely
in assembly langauge, this is not an issue. However, if you start QBX.EXE with
/L and get an INVALID FORMAT error, one of the modules in the .QLB file most
likely was a BASIC module that was not compiled with the /Fs switch.



See: LINK_with

~LINK_with - How to

After successfully compiling your BASIC source code module(s), use the LINK
program to build the EXE program file. For this example let's assume that you
have 2 BASIC source code modules just compiled and their .OBJs available, the
BULLET.LIB library, a stub object file called STUB.OBJ, and MOUSE.LIB.

To build the final stand-alone EXE from this group of files, do the following:

 C>link STUGRAD1+STUGRAD2+STUB,STUGRADE.EXE,nul,BULLET+MOUSE;

The 3 object modules, STUGRAD1, STUGRAD2, and STUB, will have their entire code
included in the EXE program, created as STUGRADE.EXE. The 2 library files,
BULLET.LIB and MOUSE.LIB, will have only the code that is actually referenced
by the 3 object modules included into the EXE program.

If successful, link creates STUGRADE.EXE ready to be run at the DOS prompt.







See: call_BULLET  AtExitXB

~call_BULLET - How to?

BULLET is called through a single entry point. The only argument passed to it
is a segmented far pointer to the control pack. The first two entries in this
pack are the function to be performed and the function return status. BULLET
is a FUNCTION call returning an INTEGER status value.

Each function (or routine) uses a prescribed pack format. For example, some
routines need only know the handle of the file, along with the function number
itself. So, to flush a data file, for example, you would do the following:

 DIM HP AS HandlePack           'could also DIM SHARED

 HP.Func = FLUSHDHXB            'FLUSHDHXB is defined as a CONST in BULLET.BI
 HP.Handle = File2FlushHandle   'the handle as returned from the Open() routine
 stat = BULLET(HP)              'do the actual call to BULLET

┌─────────────────────────────────────────────────────────────────────────────┐
│The value of stat is set to the completion code as returned by the FlushDHXB │
│routine. It is the same as the value returned in HP.Stat *IN ALL BUT A FEW*  │
│cases: InsertXB, UpdateXB, ReindexXB, and LockXB. These routines return not  │
│the actual error code, but rather a transaction index number of the access   │
│that failed. See those routines for more information.                        │
└─────────────────────────────────────────────────────────────────────────────┘
See: is_BULLET  FlushDHXB

~Specs_Overall

BULLET is dBASE III/III+/IV .DBF-compatible. This format is compatible with a
large base of software programs including the latest database packages such as
dBASE IV and FoxPro. Spreadsheet packages such as Excel and 1-2-3 can directly
import BULLET DBF data files, too. And because of BULLET's versatility, it can
also create very non-standard data files. This may be a useful feature if data
secrecy is of concern.

BULLET requires MS-DOS 3.30 or above. It uses 19K of code/static data space and
requires at least 40K of workspace. 140K of workspace is ideal.

Overall Specifications:

              DBF           (per file)          INDEX
  ────────────────────────────      ───────────────────────────
    Max records: 16,777,215                Max nodes: 65,535
  Record length: 2-4000 (8192)              Max keys: 4,063,170
     Max fields: 128 (255)                Key length: 1-64
   Field length: 1-254 (255)          Max key fields: 16

Total open index plus data files can be up to 255. Numbers in () indicate
extended specifications.

See: Specs_DBF

~Specs_DBF

To remain compatible with other dBASE III .DBF platforms you should restrict
your data files to the following specifications:

 File ID byte: 3  (83hex if .DBF has memo field, not currently supported)
 Max record size: 4000 bytes  Max fields/rec: 128   Max field size: 254 bytes
 Allowable field name characters: A-Z and the _ (upper-case)
 Allowable field types:
  C-character, 1-254 bytes
  D-date, 8 bytes, in the format YYYYMMDD (19920531)
  L-logical, 1 byte, either space, "T" or "Y", "F" or "N"
  M-memo, 10 bytes, used as pointer into .DBT file (currently not supported)
  N-numeric, 1-19 bytes, ASCII format, uses explicit decimal if needed...
    ...decimal places may be 0, or 2 to (field size - 3) but no more than 15

Restrict all data in .DBF fields to ASCII. This means you should convert binary
data to the equivalent ASCII representation, e.g., if you have the binary value
22154, it must first be converted to the string "22154" before you can store it
to the .DBF data file. So, while your in-program code deals with binary data,
your I/O code must convert it to/from ASCII. This is a dBASE-compatibility
issue only. If you can forgo these requirements you can use binary fields, any-
character field names, record sizes to 8192 bytes, and up to 255 fields.

See: Specs_DBF_a                                                        -MORE-

~Specs_DBF_a

A dBASE III .DBF is composed of 3 sections: the header, the field descriptors,
and the data area.

The header structure (first 32 bytes of file):

    Name     Type   Offset  Meaning
 ─────────  ──────  ──────  ───────────────────────────────────────────────────
 FileID      byte        0  data file type id, 03 standard (43,63,83,88h)
 LastYR      byte        1  last update year, binary
 LastMo      byte        2  last update month, binary
 LastDA      byte        3  last update day, binary
 NoRecs      long        4  number of records in file
 HdrLen      word        8  length of header, including field descriptors, +1
 RecLen      word       10  length of data record including delete tag
 internal    byte    12-31  reserved

The last update values are updated to the current date whenever the .DBF file
is flushed or closed. Likewise, the NoRecs value is updated whenever a record
is added to the .DBF. The FileID is specified when you create the file, HdrLen
and RecLen are computed and stored when the file is created, too.


See: Specs_DBF_b                                                        -MORE-

~Specs_DBF_b

The field descriptor format (follows header, one per field):

    Name     Type   Offset  Meaning
 ─────────  ──────  ──────  ───────────────────────────────────────────────────
 FieldName   char        0  field name 10 ASCII characters, A-Z or _ (0-filled)
        0T   byte       10  field name zero-termintor (must be 0)
 FieldType   char       11  field type (C D L M N)
 internal    long       12  reserved
 FieldLen    byte       16  length of this field
 FieldDC     byte       17  decimal count
 internal    byte    18-31  reserved
      
The unused bytes in the FieldName must be set to zeroes (CHR$(0)).
 
Each field is described by a 32-byte descriptor. The first field's descriptor
starts right after the header proper, at offset +32. After the last field
descriptor is data byte ASCII 13. (Note: the orginal dBASE III has a 0 byte
following this ASCII 13.) Immediately following this is the actual record data.




See: Specs_DBF_c                                                        -MORE-

~Specs_DBF_c

The data record format:

The first record is located at offset HdrLen (from the header). The first byte
of each record is a delete tag. This tag is maintained by the BULLET routines.
A space, ASCII 32, means the record is not deleted; an asterisk, ASCII 42,
means the record has been deleted (marked as deleted, often this is used as a
method to temporarily tag records, for whatever purpose).

Following the tag is the data for each field, not delimited (i.e., the fields
run together without anything separating them). The second record is at offset
HdrLen+reclen. The start offset of any record in the file can be computed as
(recordnumber - 1) * reclen + HdrLen. All data is in ASCII form.

An EOF marker (ASCII 26) is placed at the end of the last record.








See: Specs_Index

~Specs_Index

BULLET uses a proprietary, modified b-tree index method to manage the index
files. The supported key types are:

    Type     Length  Meaning
 ──────────  ──────  ───────────────────────────────────────
 Character     1-64  ASCII, NLS, or user-supplied sort table
 Integer          2  signed or unsigned 16-bit value
 Long Int         4  signed or unsigned 32-bit value

In addition to the above types, BULLET allows for unique or duplicate keys in
the index file. If duplicates are allowed, BULLET enumerates each key with an
enumerator word (see FirstKeyXB).

The key may be composed of up to 16 character fields or substrings within those
fields. Numeric fields are considered character fields by BULLET unless the key
is set to binary (see KeyFlags). Integer or LongInt binary keys can be composed
of a single field only. The key expression is specified in text (e.g., "LNAME+
SUBSTR(FNAME,1,1)+MI") and is fully evaluated when the index file is created.

A BULLET index file is composed of 3 sections: the header, the collate-sequence
table, and the node/key entry area.

See: Specs_Index_a                                                      -MORE-

~Specs_Index_a

The header structure:

    Name     Type   Offset  Meaning
 ─────────  ──────  ──────  ───────────────────────────────────────────────────
 FileID      byte        0  index file type id, 20
 RootNode    word        1  root node number
 Keys        24bit       3  number of keys in index file
 AvalNode    word        6  node number available for reuse
 FreeNode    word        8  next free node number
 KeyLen      byte       10  key length
 NodeKeys    byte       11  number of keys that fit on a node
 CodePage    word       12  code page ID
 CtryCode    word       14  country code
 internal    byte    16-21  reserved
 KeyFlags    word       22  key flags
 KeyExprn    byte   24-159  key expression
 internal    byte      160  reserved
 KeyXFlds    byte      161  number of fields used by key (1-16)
 KeyXlate    byte  162-225  translated key expression
 internal    byte  226-253  reserved
 CTsize      word      254  collate-sequence table size

See: Specs_Index_b                                                      -MORE-

~Specs_Index_b

The collate-sequence table structure:

 table      byte   256-511  sort weight table of ASCII character 0-255

Node/key entry structure (first entry is in node #1, file offset 512):

  2A  0A 00  KEY123  7B 00 00  12 00  KEY178  B2 00 00  0C 00 ...
   1.   2.     3.       4.       5.     6.       7.       8.   9.
      
 1. Key count for that node (first byte of each node)
 2. 16-bit node back pointer (for non-leaf nodes, 0 if leaf node)
 3. First key value, "KEY123" in this case
 4. 24-bit data record pointer (low word/hi byte) 7Bh = DBF record number 123
 5. 16-bit node forward ptr/back ptr (for non-leaf nodes, 0 if leaf node)
    --in this case, it indicates that the key following KEY123 is in node# 12h
    --and also that the key before KEY178 is in that node as well
 6. Second key (here "KEY178")
 7. 24-bit data pointer (record number in DBF)
 8. 16-bit forward node pointer (for non-leaf nodes, 0 if leaf node)
 9. Repeat 6 to 8 for each key on node. (node size is 512 bytes)


See: Specs_Index_c                                                      -MORE-

~Specs_Index_c

As in many b-tree implementations, BULLET's index files maintain an average
load percentage of approximately 66%. This means that in any given node, 66% of
the available space is in use. The free space in the node is attributable to
the constant reshaping of the file as keys are inserted or deleted, causing the
nodes to be split and merged. A split will occur when an insert needs to add a
key to an already full node; a merge will occur when a neighboring node is
small enough to be merged into a just split node. This constant prune-and-graft
of the b-tree results in a node load of about 66% (50% in degenerate cases such
as with already sorted data). It's this aspect of the b-tree that makes it a
consistent performer and a widely-used method of managing index files.

The following formula can be used to determine the number of keys that an index
file can hold:

  MaxKeys = MaxNodes * MaxKeysPerNode * LoadFactor
  MaxKeys = 65535 * 509/(keylen+5) * .66

The load factor can be increased to ~95% by using the ReindexXB routine. This
load factor results in superior retrieval speeds since there are more keys on
each node. Insertion speed will be decreased, however, since splitting will
occur more frequently, though perhaps not noticeably.

See: Specs_Memory

~Specs_Memory

BULLET allocates memory on an as-needed basis. When linked to an executable
program, BULLET makes use of 17.5K of code space and about 1.5K of static
DGROUP data space. To accomodate the wide variety of compilers, BULLET's API
structure will have the linker included all of the library into your final EXE
program.

All runtime memory allocations are obtained from the operating system. This
means that if your compiler generates code to allocate all the free memory at
startup for its far heap, you need to instruct it to free up some memory for
BULLET's use. In QuickBASIC you can use SETMEM(). You can also use the linker
/CP:n option to limit the amount that the compiler-generated startup code
allocates. The EXEHDR.EXE program can also be used to modify this amount. The
linker /CP: option and the EXEHDR program both modify the EXE header. To
determine if you need to release memory use the MemoryXB routine. It reports
the operating system memory available for use by BULLET.

The amount of memory that BULLET requires is based on which routines are used.
See the next screen for a list of the routines that make malloc calls to the
operating system and how much memory they require.

(Most compilers don't grab all memory as QuickBASIC when it's using $DYNAMIC).

See: Specs_Memory_a  MemoryXB                                           -MORE-

~Specs_Memory_a

Routines making dynamic memory allocations and amount (within ± 16 bytes):

  Routine           Bytes        Basis
─────────────    ───────────  ─────────────────────────────────────────────────
InitXB                  272   permanent, released when program ends (JFTmode=1)
BackupFileXB             32K  temp, released when routine exits
CreateDXB        48+(NF*32)   temp, released when routine exits (NF=NoFields)
CreateKXB               544   temp, released when routine exits
OpenDXB     144+((1+NF)*32)   semi-permanent, released when file closed
OpenKXB                1264   semi-permanent, released when file closed
PackRecordsXB      RL to 64K  temp, released when routine exits (RL=RecLength)
ReindexXB        32K to 128K  temp, released when routine exits
UpdateXB              2K+RL   temp, released when routine exits (RL=RecLength)

For example, when BackupFileXB is called it attempts to allocate 32K from the
OS. If 32K is not available, BackupFileXB returns with an error code of 8 (DOS
error #8, not enough memory). And, for example, ReindexXB attempts to allocate
128K if available. If not, it continues in decrements of 32K, down to 32K.

Needed stack space is 4K (max) for ReindexXB. Other routines can operate with
less than 1K of stack space. In other words, stack use is minimal.

See: Specs_OS_calls

~Specs_OS_calls

BULLET makes use of the following operating system calls:

INT21/25 DOS_setvector                  INT21/44/0B DOS_setsharingretrycount
INT21/2A DOS_getdate                    INT21/48 DOS_malloc
INT21/30 DOS_version                    INT21/49 DOS_free
INT21/35 DOS_getvector                  INT21/51 DOS_getpsp
INT21/39 DOS_makedir                    INT21/56 DOS_renamefile
INT21/3D DOS_openfile                   INT21/59 DOS_getextendederror
INT21/3E DOS_closefile                  INT21/5A DOS_createtempfile
INT21/3F DOS_readfile                   INT21/5B DOS_createnewfile
INT21/40 DOS_writefile                  INT21/5C DOS_lockunlockfile
INT21/41 DOS_deletefile                 INT21/65/01 DOS_getextendedcountryinfo
INT21/42 DOS_movefileptr                INT21/65/06 DOS_getcollatesequencetable
INT21/44/09 DOS_isdriveremote           INT21/67 DOS_sethandlecount
INT21/44/0A DOS_isfileremote            INT2F/10/00 DOS_isshareinstalled

No other operating system calls are made. No BIOS calls are made.





See: Specs_Languages

~Specs_Languages

BULLET is compatible with most DOS compilers. The only requirements are that
your compiler allow you to:

 1. Call a library routine via a FAR call using PASCAL calling convention
 2. Pass a far pointer (of the parameter pack) on the stack, by value
 3. Supply far pointers to the various pack parameters
 4. Be able to return an integer value from the FAR call
    (this is optional but recommended for the transaction-based routines)

These requirements can be met with most BASIC, C, and other-language DOS
compilers.

CZ online help is currently available in BASIC and C versions. Others are
pending. You should be able to do well with either of these versions using
other-language compilers since the only difference is the source code examples.







See: Specs_OSes

~Specs_OSes

BULLET is currently available only for MS-DOS and compatible operating systems.
It requires DOS 3.3 or higher.

To provide efficient memory use, BULLET uses a single-buffer cache per index
file. The single-buffer cache also provides for very quick network access since
a minimum amount of memory needs to be flushed when releasing control of BULLET
files. For maximum speed, however, an external high-performance disk cache can
be used. Hyperdisk is a good choice (shareware, $50+). A properly configured
cache can increase BULLET's performance from 10 to 300%. The most improvement
is with the InsertXB routine. The least is with ReindexXB and PackRecordsXB,
which do most of their work in temporarily allocated memory. Hyperdisk is about
the best designed disk cache available for PCs.

If you do not use a disk cache then it's recommended that you set your BUFFERS=
statement in CONFIG.SYS to at least 20 or 30. Even without a disk cache, BULLET
is still very fast. Also, be sure to set your FILES= to the number of files
that you'll be opening at any one time. If you set FILES=20 you can have BULLET
open 14 files (CZ.COM uses 1 and DOS reserves 5 more). You can set FILES=255
allowing BULLET to open up to 249 files at one time.

                 DO NOT set FILES= to a value greater than 255.

See: Specs_Networks

~Specs_Networks

BULLET currently operates on all DOS-compatible network platforms.

Be sure to install SHARE.EXE on the local machine. If you'll be opening many
files you should extended the default SHARE file-sharing information space and
the number of locks that can performed at one time. The DOS 5.0 default is
/F:2048 and /L:20. This allocates 2K for file-sharing info space and allows 20
consecutive locks to be active. If the F: value is too low, error 5 (extended
error 32) is returned on an open attempt. If you extend the JFT in InitXB and
plan to use many files, say more than 50, be sure to extend /F: by 2K for every
50 additional files and set the /L: to the number of files you plan on having
open. If L: is too low, error 1 (ext err 36) is returned on a lock attempt.

As an example, if you'll be using 100 files, set FILES=106 in CONFIG.SYS, set
SHARE /F:4096 /L:106, and IP.JFTmode=1 for InitXB. These values are a minimum.
If you have more than one process active, you need to account for other
applications' needs also.

Note that Windows always returns a "SHARE is installed" using the DOS detection
routines used by BULLET. To determine if SHARE is actually installed, attempt
to perform a lock using one of the LockXB routines. An error code indicates
that SHARE is not installed.

See: Specs_Performance

~Specs_Performance

 Test: Reindex 1,000 to 1,000,000 records (BB_LAI10.BAS)
       DBF: extended DBF using binary sort field
       key: LONG+SIGNED+UNIQUE                        Machine: 486/33 SHO

 1Meg│                                                     *
     │
     │                                    *
     │                   Records  Time  Reindex Rate
     │       *           -------  ----  ------------
 100k│  *                   1000   < 1  1000+/sec
     │                      5000     2    2500
     │ *                   10000     4    2500
     │*                    25000     7    3571   3500+ records indexed/second!
     │                     50000    14    3571   Times in table are in seconds
  10k│*                   100000    28    3571
     │                    200000    81    2469
     │*                   500000   355    1408
     │                   1000000  1124     890
   1k│*
     └─────────┴─────────┴─────────┴─────────┴─────/\/──┴─────────┴───────────
 time (secs)  100       200       300       400       18:00     20:00 (min)

See: Specs_Perf_a                                                       -MORE-

~Specs_Perf_a

 Test: Add 1,000 to 1,000,000 records (BB_LAI10.BAS)
       DBF: extended DBF using binary sort field
       key: not indexed                               Machine: 486/33 SHO

 1Meg│                                                      *
     │
     │                         *
     │         *         Records  Time    Add Rate
     │                   -------  ----  ------------
 100k│    *                 1000   < 1  1000+/sec
     │                      5000     2    2500
     │  *                  10000     4    2500
     │*                    25000    12    2083   2000+ records added/second!
     │                     50000    24    2083   Times in table are in seconds
  10k│*                   100000    50    2000
     │                    200000   103    1942
     │*                   500000   262    1908
     │                   1000000   529    1890
   1k│*
     └─────────┴─────────┴─────────┴─────────┴─────/\/──┴─────────┴───────────
 time (secs)  100       200       300       400        8:00     10:00 (min)

See: Specs_Perf_b                                                       -MORE-

~Specs_Perf_b

 Test: Access 1,000 to 1,000,000 keys(*), keys+records(+) (BB_LGK10.BAS)
       DBF: extended DBF using binary sort field
       key: LONG+SIGNED+UNIQUE                        Machine: 486/33 SHO

 1Meg│                                         *            +
     │
     │              *                          +
     │    *        +  Keys   Time  Access Rate   Keys+Recs Time  Access Rate
     │              -------  ----  -----------   --------- ----  ------------
 100k│ *   +           1000   < 1  1000+/sec         1000   < 1  1000+/sec
     │                10000     1    10000          10000     6    1667
     │               100000    22     4545         100000    68    1471
     │               200000    49     4082         200000   144    1389
     │               500000   147     3401         500000   427    1171
  10k│*+            1000000   424     2358        1000000   948    1055
     │
     │             Times for forward/reverse (Next/Prev) were similar (± 3%)
     │+            Times in table are in seconds
   1k│*
     └─────────┴─────────┴─────────┴─────────┴─────/\/──┴─────────┴───────────
 time (secs)  100       200       300       400       15:00     17:00 (min)

See: Specs_Perf_c                                                       -MORE-

~Specs_Perf_c

 Test: Insert 1,000 random records to existing data/key files (BB_LAA10.BAS)
       DBF: extended DBF using binary sort field
       key: LONG+SIGNED+UNIQUE                        Machine: 486/33 SHO

 Base records/keys   Inserted   Time / Rate
 -----------------   --------   ----   ----
              1000       1000     15     67 records+keys inserted/second
             10000       1000     18     56 Times in table are in seconds
            100000       1000     19     53
            200000       1000     20     50

 This tests the InsertXB speed. A data file containing the base records and
 an index file containing the same number of keys has inserted into it 1000
 random keys and records. The records are added to the end of the data file
 and the keys are inserted into their appropriate place in the index file.

 Typically InsertXBs would be done online. The rate of 50 inserts/second at
 200,000 records would indicate a time of 0.02 seconds per insert on even a
 very large database (0.015 secs/insert @ 1000 records).



See: Specs_Perf_d                                                       -MORE-

~Specs_Perf_d

 Test: Update 1,000 records by changing the key field (BB_LAU10.BAS)
       DBF: extended DBF using binary sort field
       key: LONG+SIGNED+UNIQUE                        Machine: 486/33 SHO

 Base records/keys    Updated   Time / Rate
 -----------------    -------   ----   ----
              1000       1000     50     20 records+keys updated/second
             10000       1000     59     17 Times in table are in seconds
            100000       1000    108      9
            200000       1000    126      8

 This tests the UpdateXB speed. A data file containing the base records and
 an index file containing the same number of keys has the first 1000 keys and
 records changed. This forces the old data record to be replaced, the old index
 key to be deleted, and a new key for the record to be inserted in the index
 file.

 Typically UpdateXBs would be done online. The rate of 8 updates/second at
 200,000 records would indicate a time of 0.125 seconds per update on even a
 very large database (0.05 secs/update @ 1000 records).


See: Specs_Overall

~InitXB

Pack: InitPack          Src: InitXBsrc          Func:   0/System

Before using any routine you must initialize the BULLET file system.

If you want more than the standard number of file handles, set InitPack.JFTmode
to 1. This expands the current process's Job File Table to allow 255 open files
maximum.

On return the DOS version (INT21/30h) is in InitPack.DOSver. Major version in
the high byte. Minor in the low. The BULLET version (*100) is returned as is
the address of the ExitXB routine. You can use this address to register ExitXB
with your own _atexit function if your runtime library does not provide _atexit
already.

Note: _atexit is a routine available in most DOS, OS/2, and ANSI runtime
library code and is called just prior to the program ending. See AtExitXB for
information on what to do if your library does not have _atexit.





See: ExitXB

~ExitXB

Pack: ExitPack          Src: ExitXBsrc          Func:   1/System

Before ending your program you should call ExitXB to close any open BULLET
files. This also will release any memory still allocated to those files.
This restores the default keyboard break handlers if they were changed.

In normal operation you would see to closing all files yourself. However, if
your program fails to reach the programmed end, it's very possible that files
may still be left open. It is essential that you properly close all BULLET
files before ending. There are two methods to achieve this:

1. In BASIC use ON ERROR GOTO label, where label is in the main module. The
code at label would then call ExitXB.

2. Use AtExitXB to automatically register ExitXB to be executed in the normal
shut-down code of the compiler. This method is preferred.

In the QB environment you can easily stop and restart the executing program
without having reached the end of the program where you call ExitXB. Previously
opened BULLET files remain open and memory used remains allocated. Eventually
you will run out of files or out of memory. Use AtExitXB to prevent this.

See: AtExitXB

~AtExitXB

Pack: ExitPack          Src: AtExitXBsrc        Func:   2/System

Used to automatically close all BULLET files, release allocated memory, and
restore the default Break handlers when your program ends. Your compiler
generates specific code to be executed in the course of ending your program.
AtExitXB registers the ExitXB routine to be performed in this compiler-
generated code.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine is standard in most DOS, OS/2, and ANSI runtime libraries. If   │
│yours does not have _atexit, then you must link with the supplied            │
│NOATEXIT.OBJ file:                                                           │
│                                                                             │
│C>link YOURPRG + NOATEXIT, ...                                               │
└─────────────────────────────────────────────────────────────────────────────┘
You can tell if your compiler doesn't supply _atexit at link time. LINK reports
'_atexit' : unresolved external. Add NOATEXIT.OBJ as described above.

Be sure that your _atexit routine is for the medium, large, or huge memory
models since BULLET uses multiple code segments and far calls.


See: MemoryXB  ExitXB  BreakXB

~MemoryXB

Pack: MemoryPack        Src: MemoryXBsrc        Func:   3/System

This is the only BULLET routine that can be used before InitXB. It reports the
largest free block of memory available from the OS. This memory does not
include fragmented memory or UMB memory that BULLET can and will use.

┌─────────────────────────────────────────────────────────────────────────────┐
│Some compilers provide their own memory management and thus will use most or │
│all of the contiguous memory when its program loads. BULLET, however,        │
│allocates memory on an as-needed basis, directly from the OS, and releases it│
│back to the OS as soon as it is no longer needed. If the memory amount       │
│returned by MemoryXB is low, your compiler's startup code has pre-allocated  │
│all OS memory. If the memory available from the OS is less than your         │
│application needs, you need to instruct your program to release some of the  │
│memory back to the OS so BULLET can use it. For QuickBASIC and BASIC PDS, see│
│the BASIC function SETMEM().                                                 │
└─────────────────────────────────────────────────────────────────────────────┘
With DOS able to use UMB memory, memory for BULLET requests may be provided
from this region. You can use StatPack.HereSeg from StatXB to locate from which
segment address the allocations are being made. Anything above C800h is UMB.


See: BreakXB  StatXB  OpenDXB  OpenKXB

~BreakXB

Pack: BreakPack         Src: BreakXBsrc         Func:   4/System

Disables system response to Control-C and Control-Break keys preventing users
from inadvertently exiting the program without first doing a BULLET shutdown.

┌─────────────────────────────────────────────────────────────────────────────┐
│It's REQUIRED that you reinstate the default break handlers with this routine│
│before ending your program. ExitXB automatically reinstates the default break│
│handlers.                                                                    │
└─────────────────────────────────────────────────────────────────────────────┘
This routine will not disable Control-Alt-Delete (a warm-boot). If the user is
at this point, he may prefer to exit via a warm-boot rather than reset the
machine.

Also, this routine does not prevent QuickBASIC and BASIC PDS from terminating
a program at an INPUT statement. This is because these versions of BASIC are
using their own method of INPUT aside from DOS. If AtExitXB has been issued,
all BULLET files will be properly closed even if *User Break* occurs.

This routine will not surpress the ^C displayed by DOS. If you don't want the
^C to be displayed move the cursor to a location off-screen, say, row 26.

See: BackupFileXB  ExitXB

~BackupFileXB

Pack: CopyPack          Src: BackupFileXBsrc    Func:   5/System

Copy an open BULLET key or data file. BULLET repacks and reindexes files in-
place, requiring less disk space to perform the function. BackupFileXB allows
you to safely copy a file before doing this.

┌─────────────────────────────────────────────────────────────────────────────┐
│This function is recommended prior to packing a data file with PackRecordsXB │
│since the data is very valuable. There is probably little need to do so when │
│reindexing an index file since index files can be constructed very easily    │
│from the data file.                                                          │
└─────────────────────────────────────────────────────────────────────────────┘










See: StatHandleXB  PackRecordsXB  ReindexXB

~StatHandleXB

Pack: StatHandlePack    Src: StatHandleXBsrc    Func:   6/System

Get information on a DOS file handle number to determine if it is a BULLET file
and if so, if that file is a BULLET key or data file.

If the returned ID value is 0, the handle is to a BULLET index file. ID=1 then
the handle is a BULLET .DBF file. ID= -1 then the handle is not a BULLET file.















See: CreateDXB  StatDXB  StatKXB

~GetExtErrorXB

Pack: XErrorPack        Src: GetExtErrorXBsrc   Func:   7/System

Get the extended error information for the last operation. This information
includes the extended error code, the error class, the recommended action, and
the location of the error. See Errors_DOS for the extended error meaning an
Errors_DOS_c for the class, action, and locus code meanings.

Note that on fatal DOS errors, such as an open floppy drive door, the extended
error code returned is 83 - fail on INT24. This indicates that the INT24
handler was invoked by DOS and that the INT24 handler told DOS to ignore the
error. (BULLET invokes its own INT24 handler each time it accesses the DOS file
system and restores it promptly after the access.) In such cases, this extended
error code is less informative than the standard return code and, the other
'extended' information should be disregarded. (In fatal DOS errors the standard
return code IS the extended error code.)

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine returns the extended error information for the LAST DOS system  │
│error. This information remains the same until the next DOS system error.    │
└─────────────────────────────────────────────────────────────────────────────┘


See: CreateDXB  Errors_DOS

~DVmonCXB

Pack: DVmonPack         Src: DVmonCXBsrc        Func:   9/DEBUG

Control BULLET debug monitor.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine is available only in the debug engine.                          │
└─────────────────────────────────────────────────────────────────────────────┘
The monitor displays in realtime the state of a data file handle, or an index
and data file handle pair if an index handle is specified. DVmonCXB is best
used on dual-display systems in which the video output is sent to the secondary
video monitor. In any case, a 4000-byte screen image is updated in real-time.

To use the monitor, set mode=1, handle=file to monitor, and VideoSeg=segment
address of 4000-byte area. The typical VideoSeg would be to video memory. If
you have a color system as the main monitor and a mono as the secondary, set
VideoSeg=&HB000. Detail system stats are continually updated to the secondary
monitor. If you have a single monitor with at least 2 video pages, set VideoSeg
to your base address plus the page size\16, typically &HB800+(4096\16). If you
have only a single-page video system, you can allocate a 4000-byte memory area
and update the video manually by moving it to your video display (80x25).


See: CreateDXB  StatDXB  StatKXB

~CreateDXB

Pack: CreateDataPack    Src: CreateDXBsrc       Func:  10/Mid-level

Create a new BULLET .DBF data file. Before using this routine allocate a field
description array of TYPE FieldDescTYPE for at least as many fields as are in
the record.

Conventional dBASE .DBF files have a FileID=3. Other possible FileIDs that you
may come across are (in hex):

 43h \__ are special-use Xbase IV DBF files, BULLET can process these file IDs
 63h /                                    since they are similar to ID type 3
 83h --- this DBF file has an Xbase III/III+ memo field/file
 88h --- this DBF file has an Xbase IV memo field/file

┌─────────────────────────────────────────────────────────────────────────────┐
│In creating your .DBF files, specify FileID=3 to ensure compatibility across │
│Xbase versions.                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

BULLET makes no special use of the FileID byte.


See: OpenDXB  FieldDescTYPE  CreateKXB

~OpenDXB

Pack: OpenPack          Src: OpenDXBsrc         Func:  11/Mid-level

Open an existing .DBF data file for use. You need to specify two things, the
filename and the DOS file access mode. If the open succeeds, the DOS file
handle is returned. Use this handle for all further access to this file.

Each .DBF data file you open allocates 144+((1 + number of fields) * 32) bytes
for internal use. This memory is not deallocated until you close the file with
CloseDXB or execute ExitXB.

You must open the data file before you can open (or create) any of its index
files.










See: CloseDXB  OpenKXB

~CloseDXB

Pack: HandlePack        Src: CloseDXBsrc        Func:  12/Mid-level

Close an existing .DBF data file for use. Closing the file updates the file
header and deallocates the memory used by this file.

┌─────────────────────────────────────────────────────────────────────────────┐
│You MUST close all BULLET files before ending your program or file corruption│
│may occur. To ensure that all files are closed in the event of an unscheduled│
│program termination, use AtExitXB.                                           │
└─────────────────────────────────────────────────────────────────────────────┘












See: StatDXB  ExitXB  CloseKXB

~StatDXB

Pack: StatDataPack      Src: StatDXBsrc         Func:  13/Mid-level

Get basic information on the BULLET .DBF data file handle specified.
Information returned includes the number of records in the file, the record
length, number of fields per record, and the date the file was last updated.

Typically, your program will keep track of whether a particular handle belongs
to a data file or a key file. In cases where this is not possible, call the
StatHandleXB routine to determine what file type a handle is.

Note that a just-created data file will have the LastUpdate date set to 0/0/0.











See: ReadDHXB  StatKXB  StatHandleXB

~ReadDHXB

Pack: HandlePack        Src: ReadDHXBsrc        Func:  14/Mid-level

Reload the disk copy of the data header for the opened .DBF data file handle
to the internal copy.

In single-user, single-tasking systems this routine is not needed. However, in
a multi-user or multi-tasking system it's possible, and desirable, for two or
more programs to use the same data file. Consider this scenario: A data file
has 100 records. Two programs access this data file, both opening it. Program 1
locks the file, adds a new record, then flushes and unlocks the file. Program 1
knows that there are now 101 records in the file. However, Program 2 is not
aware of the changes that Program 1 made--it thinks that there are still 100
records in the file. This out-of-sync situation is easily remedied by having
Program 2 reload the data header from the file on disk.

How does Program 2 know that it needs to reload the header? It doesn't. Instead
BULLET uses a simple yet effective approach when dealing with such problems.
Whenever your program locks a file, BULLET automatically reloads the header.
Whenever you unlock a file, BULLET automatically flushes the header.



See: FlushDHXB  ReadKHXB  LockXB

~FlushDHXB

Pack: HandlePack        Src: FlushDHXBsrc       Func:  15/Mid-level

Write the internal copy of the data header for the opened .DBF data file handle
to disk. The actual write occurs only if the header has been changed.

This is to ensure that the data header on disk matches exactly the data header
that is being maintained by BULLET. Also, this routine updates the operating
system's directory entry for this file.

Assume the following: A data file with 100 records. Your program opens the data
file and adds 1 record. Physically, there are 101 records on disk. However, the
header image of the data file on disk still reads 100 records. This isn't a
problem, BULLET uses its internal copy of the data header and the internal copy
does read 101 records. BUT, if there were a system failure now, the disk image
would not get updated. After the system restart, BULLET opens the file, reads
the header and thinks that there are 100 records. You lost a record. Now, if
after that add above, your program issued a FlushDHXB, the header on disk is
refreshed with the internal copy, keeping the two in-sync. Also, the routine
updates the DOS directory entry, keeping things neat there as well. Still, it
doesn't come without cost: flushing will take additional time, therefore, you
may elect to flush periodically, or whenever the system is idle.

See: CopyDHXB  ReadDHXB  FlushKHXB  LockXB

~CopyDHXB

Pack: CopyPack          Src: CopyDHXBsrc        Func:  16/Mid-level

Copy the .DBF file structure of an open data file to another DOS file.

This routine makes it easy for you to duplicate the structure of an existing
.DBF file without having to specify all the information needed by CreateDXB.

The resultant .DBF will be exactly like the source, including number of fields
and field descriptions. It will contain 0 records.











     

See: ZapDHXB  CopyKHXB

~ZapDHXB

Pack: HandlePack        Src: ZapDHXBsrc         Func:  17/Mid-level

Delete all records for a .DBF data file.

This routine is similar to CopyDHXB except for one major difference: ALL DATA
RECORDS IN THE *SOURCE* FILE ARE PHYSICALLY DELETED, so be *careful*.

If you have a .DBF file with 100 records and use ZapDHXB on it, all 100 records
will be physically deleted and the file truncated to 0 records. There is no
return from this routine. All data is gone.

                           ┌─────────────────┐
                           │* C A U T I O N *│
                           └─────────────────┘






     

See: CreateKXB  CopyDHXB  ZapKHXB

~CreateKXB

Pack: CreateKeyPack     Src: CreateKXBsrc       Func:  20/Mid-level

Create a new BULLET key file. Before you can create a key file, you must first
have opened (and have created if necessary) the BULLET .DBF data file that it
is to index. (BULLET couples index and data files tightly.)

To create the key file, you need to provide the key expression, key flags, .DBF
file link handle, and optionally, the code page ID, country code, and collate
table.

                                Key Expression

The key expression is an ASCII character string composed of the elements that
are to make up this index file's key. The key can be composed of any or all of
the fields in the .DBF data record or sub-strings within any of those fields.
Two functions are supported in evaluating a key expression. These are SUBSTR()
and UPPER(). SUBSTR() is similar to BASIC's MID$() in that it extracts part of
a string starting at a particular position for x number of characters. UPPER()
is similar to UCASE$() in that it converts all lower-case letters to their
upper-case equivalent. Since BULLET supports NLS, UPPER() conversion is not
required for proper sorting of mixed-case text strings.

See: CreateKXB_a  CreateDXB                                             -MORE-

~CreateKXB_a

All names used in the key expression must be a valid field name in the DBF data
file. Some sample key expressions given that the .DBF has the following fields:

 Fields...             Valid key expressions
 ──────────────     ──────────────────────────
 FNAME C 25 0       kx = "LNAME"
 LNAME C 25 0       kx = "LNAME+FNAME"
 SSN   C  9 0       kx = "SUBSTR(LNAME,1,4)+SUBSTR(FNAME,1,1)+SUBSTR(SSN,6,4)
 DEPT  N  5 0       kx = "UPPER(LNAME+FNAME)"  (for non-NLS index files)
  :     :           kx = "DEPT+SSN" (N- + C-type is valid for non-binary keys)

                                    Key Flags

The key expression is used in conjunction with the key flags to determine the
type of key generated.

First, if your index file is to disallow duplicate keys, add 1 to KeyFlag.

If you have a key composed of a character field(s) or portions thereof, you
specify a KeyFlag = 2. This instructs BULLET that the sort order is left-to-
right (proper mixed-case sorting is available, see code page ID).

See: CreateKXB_b                                                        -MORE-

~CreateKXB_b

If you have a key composed of a numeric field(s) or portions thereof, you can
also specify a KeyFlag = 2. This instructs BULLET to treat the numeric field
as a regular character field for sorting. To ensure proper sorting, you must
decimal-align the +numeric strings in the .DBF data field, i.e., right-justify
the numeric strings (dBASE .DBF numeric strings are stored as ASCII strings).
These non-binary numeric fields are just like character fields to BULLET.

In addition, if you have a key composed of a SINGLE numeric field (fld type N)
and the field is an integer (NO DECIMAL POINT), you can specify a KeyFlag of 16
or 32. KeyFlag=16 is for a field known to be in word/integer range; KeyFlag=32
if the field is known to be in LongInt range. These KeyFlag values instruct
BULLET to sort the key as a 16/32-bit BINARY value. It also stores the key as a
16- or 32-bit value (only 2 or 4 bytes) in the index, eventhough the data field
is in ASCII (keyflag=16 or 32).

Although not dBASE compatible, you may use BINARY FIELDS in your data records.
dBASE always has ASCII data in the data fields, even if the field is numeric.
For example, an N type field of 8.2 is stored as an ASCII text string in the
data record, say, a string like " 1100.55". If you want dBASE compatibility
your field data must also be ASCII. However, if you can forgo this requirement,
you can use binary values in the fields.

See: CreateKXB_c                                                        -MORE-

~CreateKXB_c

To do this you must specify a field type of "B" (actually, anything but a "N")
and, IF IT IS TO BE USED AS A KEY FIELD, also set the 16- or 32-bit KeyFlag.
Unique and signed may also be flagged. The field length for a "B" field type is
2 or 4. Make sure the key flags match (2 if cINTEGER, 4 if cLONG).

If you specify a binary key flag (for either N or B field types), you must also
specify whether the field is to be treated as a signed or unsigned value. If
values less than 0 are possible, add to KeyFlag the hex number &H8000.
 
 KeyFlag = cUNIQUE + cCHAR              'unique character key (NLS sort)
 KeyFlag = cINTEGER + cUNIQUE           'unique unsigned integer (binary sort)
 KeyFlag = cUNIQUE + cSIGNED + cLONG    'unique signed long
 KeyFlag = cCHAR                        'character key with duplicates allowed
 KeyFlag = cCHAR + cINTEGER             'THIS IS AN INVALID KEY FLAGS!
 KeyFlag = cLONG                        'unsigned long (dups allowed)

The following values are defined in BULLET.BI (and BULLET.H):

  cUNIQUE=1, cCHAR=2, cINTEGER=&H10, cLONG=&H20, cNLS=&H4000, cSIGNED=&H8000

The NLS flag is assigned by BULLET. StatKXB is used to query KeyFlags.

See: CreateKXB_d                                                        -MORE-

~CreateKXB_d

The key expression you specify may be up to 136 characters, and evaluate out to
64 bytes (62 bytes if unique key is not specified). I.e, kx$ = "SUBSTR(..." can
be up to 136 characters, and that the actual key built from this expression can
be no longer that 64 bytes, or 62 if you did not specify UNIQUE. In general,
shorter keys (the key itself, not the expression) offer better performance.

                         DBF File Link Handle (XBlink)

Since BULLET evaluates the key expression at CreateKXB, it must have access to
the DBF file to verify that the key expression is valid. You must therefore
supply CreateKXB with the OS file handle of the opened DBF data file.

National Language Support (NLS)

With DOS 3.3 and later, NLS is available. BULLET uses NLS to build the collate
sequence table that it uses to ensure proper sorting of mixed-case keys as well
as the sorting of foreign language alphabets. In order for BULLET to use the
proper collate table, it must know what code page ID and coutry code to use.
This table is made part of the index file so that all subsequent access to the
index file maintains the original sort order, even if the MIS shop is moved to
another location/computer system using another country code/code page.

See: CreateKXB_e                                                        -MORE-

~CreateKXB_e

                                Code Page ID

To use the default code page ID of the computer in use, specify a code page ID
of -1. This instructs BULLET to use the collate-sequence table as provided by
MS-DOS running on the machine. You may also specify the code page ID for BULLET
to use, but only if support for the code page ID is available on your machine.
Look in your DOS manual under CUSTOMIZING FOR INTERNATIONAL USE for specific
code page IDs and country codes. See also the COUNTRY and NLSFUNC commands.
You may also specify a code page ID = 0 in which case no collate table is used.

                                Country Code

To use the default country code of the computer in use, specify a country code
of -1. This instructs BULLET to use the collate-sequence table as provided by
MS-DOS running on the machine. You may also specify the country code for BULLET
to use, but only if support for the country code is available on your machine.
Look in your DOS manual under CUSTOMIZING FOR INTERNATIONAL USE for specific
code page IDs and country codes. See also the COUNTRY and NLSFUNC commands.
You may also specify a country code = 0 in which case no collate table is used.

Typically, you set CodePageID = -1, CoutryCode = -1 and CollatePtr = 0.

See: CreateKXB_f                                                        -MORE-

~CreateKXB_f

                         User-specified Collate Table

If you are to use a MS-DOS supplied collate table (BOTH codepage ID and country
codes are non-zero) then you do not need to specify a collate table--DOS will.
The option to allow a user-specified collate table is to work around some DOS
versions supplying incorrect collate tables. If you find that the DOS-supplied
collate table is not valid (it's stored in the second sector of the file) for
your country, you can supply the table to be used by pointing the CollatePtr
variables to your in-memory version of a valid collate table. If you want to
use the DOS-supplied collate table, you MUST set the CollatePtr variables = 0.

Note: The collate table is a 256-byte table that contains the sort value of
each character (0-255). For example, the first byte would be 0, second would
be 1, and so on. Values for characters up to the lower-case letters (ASCII 97)
are usually as you would expect: "A" has a value of 65. However, the lower-case
letters have the same value as their upper-case counterparts: "a" also has a
value of 65. BULLET uses this collate table to ensure proper sorting.

If you specify EITHER code page ID OR country code = 0 then no collate table
is used or built. Instead, sorting is done by standard ASCII sort. This is
somewhat faster but less versatile. Use UPPER() for mixed-case sort if needed.

See: OpenKXB  CreateKXB

~OpenKXB

Pack: OpenPack          Src: OpenKXBsrc         Func:  21/Mid-level

Open an existing key file for use.

Each key file that you open allocates 1264 bytes for internal use. This memory
is not deallocated until you close the file with CloseKXB or execute ExitXB.

You must open the data file before you can open its related index file
because you must supply the handle of the data file that this index files
indexes.












See: CloseKXB  OpenDXB

~CloseKXB

Pack: HandlePack        Src: CloseKXBsrc        Func:  22/Mid-level

Close an open key file. Closing the file updates the file header and
deallocates the memory used by this file.

┌─────────────────────────────────────────────────────────────────────────────┐
│You MUST close all BULLET files before ending your program or file corruption│
│may occur. To ensure that all files are closed on the event of an unscheduled│
│program termination, use AtExitXB.                                           │
└─────────────────────────────────────────────────────────────────────────────┘












See: StatKXB  ExitXB  CloseDXB

~StatKXB

Pack: StatKeyPack       Src: StatKXBsrc         Func:  23/Mid-level

Get basic information on a BULLET key file handle specified. Information
returned includes the number of keys in the file, the key length, the data file
handle for this key, the last accessed record number of that data file, NLS
information, and the key flags.

Typically, your program will keep track of whether a particular handle belongs
to a key file or a data file. In cases where this is not possible, call the
StatHandleXB routine to determine what file type a handle is.












See: ReadKHXB  StatDXB  StatHandleXB

~ReadKHXB

Pack: HandlePack        Src: ReadKHXBsrc        Func:  24/Mid-level

Reload the disk copy of the key header for the opened key file handle to the
internal copy.

In single-user, single-tasking systems this routine is not needed. However, in
a multi-user or multi-tasking system it's possible, and desirable, for two or
more programs to use the same data file. Consider this scenario: A key file has
100 keys. Two programs access this key file, both opening it. Program 1 locks
the file, adds a new key, then flushes and unlocks the file. Program 1 knows
that there are now 101 keys in the file. However, Program 2 is not aware of the
changes that Program 1 made--it thinks that there are still 100 keys in the
file. This out-of-sync situation is easily remedied by having Program 2 reload
the key header from the file on disk.

How does Program 2 know that it needs to reload the header? It doesn't. Instead
BULLET uses a simple yet effective approach when dealing with such problems.
Whenever your program locks a file, BULLET automatically reloads the header.
Whenever you unlock a file, BULLET automatically flushes the header.



See: FlushKHXB  ReadDHXB  FlushDHXB  LockXB

~FlushKHXB

Pack: HandlePack        Src: FlushKHXBsrc       Func:  25/Mid-level

Write the internal copy of the key header for the opened key file handle to
disk. The actual write occurs only if the header has been changed.

This is to ensure that the key header on disk matches exactly the key header
that is being maintained by BULLET. Also, this routine updates the operating
system's directory entry for this file.
     
Assume the following: A data file with 100 keys. Your program opens the key
file and adds 1 key. Physically, there are 101 keys on disk. However, the
header image of the data file on disk still reads 100 keys. This isn't a
problem, BULLET uses its internal copy of the key header and the internal copy
does read 101 keys. BUT, if there were a system failure now, the disk image
would not get updated. After the system restart, BULLET opens the file, reads
the header and thinks that there are 100 keys. You lost a key. Now, if after
that add above, your program issued a FlushKHXB, the header on disk is
refreshed with the internal copy, keeping the two in-sync. Also, the routine
updates the DOS directory entry, keeping things neat there as well. Still, it
doesn't come without cost: flushing will take additional time, therefore, you
may elect to flush periodically, or whenever the system is idle.

See: CopyKHXB  ReadKHXB  FlushDHXB  LockXB

~CopyKHXB

Pack: CopyPack          Src: CopyKHXBsrc        Func:  26/Mid-level

Copy the key file structure of an open key file to another DOS file.

This routine makes it easy for you to duplicate the structure of an existing
key file without having to specify all the information needed by CreateKXB.

The resultant key file will be exactly like the source, including key flags and
key expression. It will contain 0 keys.











     

See: ZapKHXB  CopyKHXB

~ZapKHXB

Pack: HandlePack        Src: ZapKHXBsrc         Func:  27/Mid-level

Delete all keys for a key file.

This routine is similar to CopyKHXB except for one major difference: ALL KEYS
IN THE *SOURCE* FILE ARE PHYSICALLY DELETED, so be *careful*.

If you have a key file with 100 keys and use ZapKHXB on it, all 100 keys will
be physically deleted and the file truncated to 0 keys. There is no return from
this routine. All data is gone.

                           ┌─────────────────┐
                           │* C A U T I O N *│
                           └─────────────────┘






     

See: GetDescriptorXB  CopyKHXB  ZapDHXB

~GetDescriptorXB

Pack: DescriptorPack    Src: GetDescriptorXBsrc Func:  30/Mid-level

Get the field descriptor information for a field.

You can specifiy either the fieldname or the field number (position of the
field within the record where the first field is #1) to get info on.

The field descriptor contains the following information:

  FIELDNAME  10 upper-case characters, A-Z and _ allowed, unused space is
             0-filled and is 0-terminated (11 bytes, ASCII, byte 11 always=0)

  FIELDTYPE  single ASCII character where C=character, N=numeric, D=date,
             L=logical, and M=memo field (1 byte, ASCII)

  FIELDLEN   length of field: C=1-254, N=1-19, D=8 (yyyymmdd), L=1 (T/F/space),
             M=10, this is total field length (1 byte, binary)

  FIELDDC    places right of decimal point if N field type, minimum if not 0 is
             2, can be up to 6 or 8, non-N fields always 0 (1 byte, binary)


See: GetRecordXB

~GetRecordXB

Pack: AccessPack        Src: GetRecordXBsrc     Func:  31/Mid-level

Get the physical record from the data file into a data buffer by record number.

The data buffer is typically a TYPEd variable defined as the .DBF record itself
is defined. For example, if the DBF record has 2 fields, LNAME and FNAME, then
variable would be TYPEd as:

  TYPE RecBuffer
  tag AS STRING * 1             'the Xbase DBF delete flag (must be included)
  LastName AS STRING * 25       'same field length as the .DBF LNAME field
  FirstName AS STRING * 25      'same field length as the .DBF FNAME field
  END TYPE

This method of accessing the data file does not use any indexing. Therefore, it
typically is not used except for special purposes. The preferred method to
access the data is by one of the keyed Get() routines.





See: AddRecordXB  GetEqualXB

~AddRecordXB

Pack: AccessPack        Src: AddRecordXBsrc     Func:  32/Mid-level

Append the record in the data buffer to the end of the DBF file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This method of adding a record does not involve any indexing. It is typically│
│used to build a data file en masse and do the indexing after the entire .DBF │
│file(s) has been built.                                                      │
└─────────────────────────────────────────────────────────────────────────────┘
If you have several thousand data records to be added at once, this method of
building the DBF first and then using the ReindexXB routine is often faster
than using the InsertXB routine for each record to add.

The AddRecordXB is very fast. 400 recs/sec on an AT machine is typical. Over
2000 recs/sec can be added on a fast 486 machine--that's 120,000 records added
per minute.

The record number used is determined by BULLET and it is returned in AP.RecNo.




See: UpdateRecordXB  InsertXB  ReindexXB

~UpdateRecordXB

Pack: AccessPack        Src: UpdateRecordXBsrc  Func:  33/Mid-level

Write the updated data record to the the physical record number.

┌─────────────────────────────────────────────────────────────────────────────┐
│This method of writing the updated record must not be used if any field(s) in│
│the record is used as a key field(s) and has been changed.                   │
└─────────────────────────────────────────────────────────────────────────────┘
This method of updating a record is very fast if you know that that update is
not going to alter any field used as a key in any index file that uses it. You
must, of course, first get the data record into the record buffer. Then you can
change it and write the update out to disk with this routine.

If you need to change a field(s) that is used as a key field or part of one,
use the UpdateXB routine. UpdateXB not only dynamically updates all related
index files if you change a key field, it also will undo any and all changes if
an error occurs in the transaction.





See: DeleteRecordXB  GetRecordXB  UpdateXB

~DeleteRecordXB

Pack: AccessPack        Src: DeleteRecordXBsrc  Func:  34/Mid-level

Tag the record at the physical record number as being deleted.

┌───────────────────────────────────────────────────────────────────────────┐
│This does not tag any in-memory copies of the record so be sure to mark any│
│such copies as being deleted yourself.                                     │
└───────────────────────────────────────────────────────────────────────────┘
The first byte of every .DBF record is reserved for the delete tag. This tag
is a space (ASCII 32) if the record is normal, or a * (ASCII 42) if it's marked
as being deleted. This delete tag is a reserved field in the DBF record and as
such is not defined as a formal field with a descriptor, etc. Make sure that
you define your in-memory buffers to reserve the first byte for the delete tag.

The Xbase DBF standard doesn't physically remove records marked as deleted from
the data file. It doesn't mark them as available/reusable either. To physically
remove records marked as deleted use PackRecordsXB.

Records can be temporarily marked as deleted then recalled to normal status.
The Key/Get routines (GetFirstXB, etc.) return the record number needed for
this routine after each access in AP.RecNo.

See: UndeleteRecordXB  PackRecordsXB

~UndeleteRecordXB

Pack: AccessPack        Src: UndeleteRecordsrc  Func:  35/Mid-level

Tag the record at the physical record number as being normal (not deleted).

┌───────────────────────────────────────────────────────────────────────────┐
│This does not tag any in-memory copies of the record so be sure to mark any│
│such copies as being normal yourself.                                      │
└───────────────────────────────────────────────────────────────────────────┘
The first byte of every .DBF record is reserved for the delete tag. This tag
is a space (ASCII 32) if the record is normal, or a * (ASCII 42) if it's marked
as being deleted. This delete tag is a reserved field in the DBF record and as
such is not defined as a formal field with a descriptor, etc. Make sure that
you define your in-memory buffers to reserve the first byte for the delete tag.

The Xbase DBF standard does not physically remove records marked as deleted
from the data file so you can "recall" then back to normal status as easily as
you marked them deleted.





See: PackRecordsXB  DeleteRecordXB

~PackRecordsXB

Pack: AccessPack        Src: PackRecordsXBsrc   Func:  36/Mid-level

Rebuild the open DBF file by physically removing all records marked as deleted.

┌─────────────────────────────────────────────────────────────────────────────┐
│Packing occurs in place using the existing file. It's recommended that you   │
│use BackupFileXB to copy the current DBF file before using this routine in   │
│case of a failure during the pack process.                                   │
└─────────────────────────────────────────────────────────────────────────────┘
The newly packed file is truncated to reflect the current, actual size.

If there are index files for this .DBF file, they MUST all be reindexed after
the pack process by using ReindexXB.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine dynamically allocates at least as many bytes as the length of   │
│the record. More if available.                                               │
└─────────────────────────────────────────────────────────────────────────────┘




See: FirstKeyXB  DeleteRecordXB  BackupFileXB  ReindexXB

~FirstKeyXB

Pack: AccessPack        Src: FirstKeyXBsrc      Func:  40/Mid-level

Retrieve the first key in index order from the index file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine does not access the .DBF file and so does not retrieve the data │
│record. What it does do is locate the first key of the index, returning it,  │
│and also returning the record number within the .DBF that the key indexes.   │
└─────────────────────────────────────────────────────────────────────────────┘
To retrieve the data record you can use the GetRecordXB routine. The preferred
method, however, is to use the GetFirstXB.

The key returned includes an enumerator if a non-unique index file is involved.
┌─────────────────────────────────────────────────────────────────────────────┐
│The enumerator is a little-endian 16-bit value that serves to differentiate  │
│up to 65535 "identical", non-unique keys. It is attached to all keys of non- │
│unique index files and occupies the last two bytes of the key.               │
└─────────────────────────────────────────────────────────────────────────────┘
This routine is typically used to position the index file to the first key so
as to allow forward in-order access to the keys by using NextKeyXB.


See: EqualKeyXB  GetFirstXB  GetRecordXB

~EqualKeyXB

Pack: AccessPack        Src: EqualKeyXBsrc      Func:  41/Mid-level

Search for the exact key in the index file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine does not access the .DBF file and so does not retrieve the data │
│record. What it does do is search for the key in the index, and if found,    │
│returns the record number within the .DBF that the key indexes. The key must │
│be an exact match, including enumerator word if a non-unqiue index file.     │
└─────────────────────────────────────────────────────────────────────────────┘
To retrieve the data record you can use the GetRecordXB routine. The preferred
method, however, is to use the GetEqualXB.

This routine will only find EXACT matches to the specified key (including the
enumerator if applicable). However, even if the exact key is not found in the
index file, the index file is positioned so that the next NextKeyXB retrieves
the key that would have followed the unmatched specified key. For example,
if the key to match was "KINGS" (a partial key in this case), EqualKeyXB would
return a key not found error. If you were to now do a NextKeyXB, the next key
would be returned, let's say it is "KINGSTON".


See: NextKeyXB  GetEqualXB  GetRecordXB

~NextKeyXB

Pack: AccessPack        Src: NextKeyXBsrc       Func:  42/Mid-level

Retrieve the next key in index order from the index file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine does not access the .DBF file and so does not retrieve the data │
│record. What it does do is retreive the next key of the index, returning it, │
│and also returning the record number within the .DBF that the key indexes.   │
└─────────────────────────────────────────────────────────────────────────────┘
To retrieve the data record you can use the GetRecordXB routine. The preferred
method, however, is to use the GetNextXB.

The key returned includes an enumerator if a non-unique index file is involved.

This routine is typically called after the index file has first been positioned
to a known key using either FirstKeyXB or EqualKeyXB, or after a previous
NextKeyXB or even PrevKeyXB. What it basically does is get the key following
the current key, and then make that key the new current key.




See: PrevKeyXB  GetNextXB  GetRecordXB

~PrevKeyXB

Pack: AccessPack        Src: PrevKeyXBsrc       Func:  43/Mid-level

Retrieve the previous key in index order from the index file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine does not access the .DBF file and so does not retrieve the data │
│record. What it does do is retreive the previous key of the index, returning │
│it and also returning the record number within the .DBF that the key indexes.│
└─────────────────────────────────────────────────────────────────────────────┘
To retrieve the data record you can use the GetRecordXB routine. The preferred
method, however, is to use the GetPrevXB.

The key returned includes an enumerator if a non-unique index file is involved.

This routine is typically called after the index file has first been positioned
to a known key using either LastKeyXB or EqualKeyXB, or after a previous
PrevKeyXB or even NextKeyXB. What it basically does is to get the key previous
the current key, and then make that key the new current key.




See: LastKeyXB  GetPrevXB  GetRecordXB

~LastKeyXB

Pack: AccessPack        Src: LastKeyXBsrc       Func:  44/Mid-level

Retrieve the last key in index order from the index file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine does not access the .DBF file and so does not retrieve the data │
│record. What it does do is locate the last key of the index, returning it,   │
│and also returning the record number within the .DBF that the key indexes.   │
└─────────────────────────────────────────────────────────────────────────────┘
To retrieve the data record you can use the GetRecordXB routine. The preferred
method, however, is to use the GetLastXB.

This routine is typically used to position the index file to the last key so as
to allow reverse in-order access to the keys by using PrevKeyXB.








See: StoreKeyXB  GetLastXB  GetRecordXB

~StoreKeyXB

Pack: AccessPack        Src: StoreKeyXBsrc      Func:  45/Mid-level

Insert the key into the index file in proper key order.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine does not add the data record to the .DBF file. It only inserts  │
│the key and record number into the index file. Use InsertXB, instead.        │
└─────────────────────────────────────────────────────────────────────────────┘
To do a complete data record and key insert, you could use AddRecordXB to add
the data record to the .DBF, BuildKeyXB to construct the key, then StoreKeyXB
to insert the key and record number information into the index file. If that
key already exists and the file allows duplicate keys, you need to attach the
proper enumerator word and retry StoreKeyXB.

This is much too much to do. Instead, just use InsertXB. All these details
including adding the data record and multi-key inserts are performed
automatically with just the single call.





See: DeleteKeyXB  InsertXB

~DeleteKeyXB

Pack: AccessPack        Src: DeleteKeyXBsrc     Func:  46/Mid-level

Physically remove the specified key from the index file.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine requires an EXACT key match for all bytes of the key, including │
│the enumerator word if a non-unique index file is involved.                  │
└─────────────────────────────────────────────────────────────────────────────┘
This routine would seldom be used, typically, since deleted dBASE data records
are only physically deleted during a PackRecordsXB and the index file is
rebuilt afterward using ReindexXB.











See: BuildKeyXB  CurrentKeyXB

~BuildKeyXB

Pack: AccessPack        Src: BuildKeyXBsrc      Func:  47/Mid-level

Build the key for the specifed data record based on the key expression for the
index file. If the index file is non-unique, a 0-value enumerator is attached.

┌─────────────────────────────────────────────────────────────────────────────┐
│The enumerator is a little-endian 16-bit value that serves to differentiate  │
│up to 65535 "identical", non-unique keys. It is attached to all keys of non- │
│unique index files and occupies the last two bytes of the key.               │
└─────────────────────────────────────────────────────────────────────────────┘
This routine, like most of the mid-level routines, typically would not be used
since the high-level access routines take care of this detail automatically.










See: CurrentKeyXB  StoreKeyXB

~CurrentKeyXB

Pack: AccessPack        Src: CurrentKeyXBsrc    Func:  48/Mid-level

Retrieve the current key value for the specified key file handle and also the
data record number that it indexes.

This routine is useful in that it retrieves on demand the actual key value of
the last accessed key in the index file (and the data record number). Most
often you don't need this information so it would be a waste of time and space
for your program to explicitly track each current key for each index file that
you have open.












See: GetFirstXB  ReindexXB  DeleteKeyXB

~GetFirstXB

Pack: AccessPack        Src: GetFirstXBsrc      Func:  60/High-level

Retrieve the first indexed key and data record.

The key returned includes an enumerator if a non-unique index file is involved.

This routine is typically used to process a database in index order starting at
the first ordered key (and its data record). After processing this first entry,
subsequent in-order access of the database is achieved by using GetNextXB until
the end of the database is reached.

This routine, like all the high-level Get routines, fills in the AP.RecNo of
the record accessed. In GetFirstXB's case, it fills AP.RecNo with the record
number pointed to by the first key. Since this is so, the AP pack is primed for
an UpdateXB after each high-level Get. Other methods to get the record number
are to use CurrKeyXB or any of the Key routines (KeyFirstXB, etc.).






See: GetEqualXB  FirstKeyXB  UpdateXB

~GetEqualXB

Pack: AccessPack        Src: GetEqualXBsrc      Func:  61/High-level

Search for the exact key in the index file and return its data record.

This routine will only find EXACT matches to the specified key (including the
enumerator if applicable). However, even if the exact key is not found in the
index file, the index file is positioned so that the next GetNextXB retrieves
the key that would have followed the unmatched specified key. For example,
if the key to match was "KINGS" (a partial key in this case), GetEqualXB would
return a key not found error. If you were to now do a GetNextXB, the next key
and data record would be returned, let's say the key is "KINGSTON" and its data
record is the data record for that key. Another GetNextXB would retrieve the
key and record after that. (GetPrevXB can be used in this fashion too.)

This routine, like all the high-level Get routines, fills in the AP.RecNo of
the record accessed. In GetEqualXB's case, it fills AP.RecNo with the record
number pointed to by the matched key. Since this is so, the AP pack is primed
for an UpdateXB after each high-level Get. Other methods to get the record
number are to use CurrKeyXB or any of the Key routines (KeyEqualXB, etc.).



See: GetNextXB  EqualKeyXB

~GetNextXB

Pack: AccessPack        Src: GetNextXBsrc       Func:  62/High-level

Retrieve the next indexed key and its data record.

The key returned includes an enumerator if a non-unique index file is involved.

This routine is typically calld after the index file has first been positioned
to a known key using either GetFirstXB or GetEqualXB, or after a previous
GetNextXB or even GetPrevXB. What it basically does is get the key and data
record following the current key, and then make that key the new current key.

This routine, like all the high-level Get routines, fills in the AP.RecNo of
the record accessed. In GetNextXB's case, it fills AP.RecNo with the record
number pointed to by the next key. Since this is so, the AP pack is primed for
an UpdateXB after each high-level Get. Other methods to get the record number
are to use CurrKeyXB or any of the Key routines (KeyNextXB, etc.).






See: GetPrevXB  NextKeyXB

~GetPrevXB

Pack: AccessPack        Src: GetPrevXBsrc       Func:  63/High-level

Retrieve the previous indexed key and record.

The key returned includes an enumerator if a non-unique index file is involved.

This routine is typically called after the index file has first been positioned
to a known key using either GetLastXB or GetEqualXB, or after a previous
GetPrevXB or even GetNextXB. What it basically does is to get the key and data
record previous the current key, and then make that key the new current key.

This routine, like all the high-level Get routines, fills in the AP.RecNo of
the record accessed. In GetPrevXB's case, it fills AP.RecNo with the record
number pointed to by the previous key. Since this is so, the AP pack is primed
for an UpdateXB after each high-level Get. Other methods to get the record
number are to use CurrKeyXB or any of the Key routines (KeyPrevXB, etc.).






See: GetLastXB  PrevKeyXB

~GetLastXB

Pack: AccessPack        Src: GetLastXBsrc       Func:  64/High-level

Retrieve the last indexed key and record.

This routine is typically used to process a database in reverse index order
starting at the last ordered key (and its data record). After processing this
last entry, subsequent reverse-order access of the database is achieved by
using GetPrevXB until the top of the database is reached.

This routine, like all the high-level Get routines, fills in the AP.RecNo of
the record accessed. In GetLastXB's case, it fills AP.RecNo with the record
number pointed to by the last key. Since this is so, the AP pack is primed for
an UpdateXB after each high-level Get. Other methods to get the record number
are to use CurrKeyXB or any of the Key routines (KeyLastXB, etc.).








See: InsertXB  LastKeyXB

~InsertXB

Pack: AccessPack        Src: InsertXBsrc        Func:  65/High-level

Add the data record to data file and insert the related key(s) into the linked
index file(s).

This routine is used to add new entries into a database, one at a time. The
data record is first added to the data file, then for each related index file,
a key is inserted into the appropriate index file. Up to 32 index files can be
automatically maintained for each data file.

This and several other routines are transaction-based. If a failure occurs
prior to the routine's completion, all changes made to the database by the
routine will be backed-out and the database (data and related index file(s))
effectively restored to its original state.
                                  
┌─────────────────────────────────────────────────────────────────────────────┐
│If the routine failed to complete, the function return value is the number of│
│the pack that caused the failure. The pack's Stat is checked to determine the│
│error code. If the function return value is 0, YOU MUST STILL check the first│
│pack's Stat. If it's non-zero, then the failure occured with the data record.│
└─────────────────────────────────────────────────────────────────────────────┘

See: UpdateXB  StoreKeyXB

~UpdateXB

Pack: AccessPack        Src: UpdateXBsrc        Func:  66/High-level

Modify an existing data record (identified by record number) and automatically
perform any index file updates needed to keep the index file(s) in sync.

If any key fields changed between the original record and the new one, this
routine updates the appropriate index file(s) by replacing the original key(s)
with new the key(s) based on the updated data record. Up to 32 index files can
be automatically maintained for each data file. Get routines (GetFirstXB, etc.)
set the AP.RecNo of the record that UpdateXB uses.

This and several other routines are transaction-based. If a failure occurs
prior to the routine's completion, all changes made to the database by the
routine will be backed-out and the database (data and related index file(s))
effectively restored to its original state.

┌─────────────────────────────────────────────────────────────────────────────┐
│If the routine failed to complete, the function return value is the number of│
│the pack that caused the failure. The pack's Stat is checked to determine the│
│error code. If the function return value is 0, YOU MUST STILL check the first│
│pack's Stat. If it's non-zero, then the failure occured with the data record.│
└─────────────────────────────────────────────────────────────────────────────┘
See: ReindexXB  UpdateRecordXB

~ReindexXB

Pack: AccessPack        Src: ReindexXBsrc       Func:  67/High-level

Reindex all related index files for a data file.

The index file(s) must already exist and be open. Any existing key data is
overwritten by the new key data. In other words, if you have a 10MByte index
file, ReindexXB uses the same file space building the news keys over the old.
This results in a less fragmented disk and also minimizes disk space needed.
You can also create a new, empty index file and reindex to that. This would be
useful, for instance, if you needed to create a temporary index file--something
that you'd use for a report, say, then delete after the report.

This routine creates a TEMPORARY work file in either the current directory or,
if the DOS environment variable TMP is defined, in the TMP= directory. The size
of this file is approx. bytes = (RECORDS * (KEYLEN+6)). ReindexXB can operate
in as little as 32K of available memory and can use up to 128K. The resultant
index file(s) are optimized for minimum size AND maximum retrieval speed.
┌─────────────────────────────────────────────────────────────────────────────┐
│If the routine failed to complete, the function return value is the number of│
│the pack that caused the failure. The pack's Stat is checked to determine the│
│error code. A return value of zero indicates no error occured.               │
└─────────────────────────────────────────────────────────────────────────────┘
See: LockXB  PackRecordsXB

~LockXB

Pack: AccessPack        Src: LockXBsrc          Func:  80/Network

Lock all bytes in the index file handle(s) for exclusive use by the current
process and reload the index file header(s) from disk. Also lock all bytes in
the related data file and reload the data file header from disk.

┌─────────────────────────────────────────────────────────────────────────────┐
│The files must have been opened with the appropriate share attribute and not │
│in compatibility mode. SHARE.EXE MUST be installed or DOS error 1 is issued. │
├─────────────────────────────────────────────────────────────────────────────┤
│This routine is transaction-based and will lock all index files specified in │
│AccessPack and the data file. If any lock fails, all previous locks by this  │
│routine are released. The return value indicates which access pack failed, if│
│any. This value is used as the index into the AccessPack group for you to    │
│identify the error code. See LockXBsrc for determining this exactly.         │
└─────────────────────────────────────────────────────────────────────────────┘
Use the DriveRemoteXB and/or FileRemoteXB to determine if locking is necessary.
If the files are on a remote drive then it is best to use locking. Locking may
also be necessary on multitasking local machines accessing shared files.

This routine is a combination of LockKeyXB and LockDataXB.

See: UnlockXB  LockKeyXB  LockDataXB  DriveRemoteXB  FileRemoteXB

~UnlockXB

Pack: AccessPack        Src: UnlockXBsrc        Func:  81/Network

Unlock all bytes in the specified file handle(s) (previously locked) and flush
the file header(s) to disk (flush done before lock(s) released). Also unlock
all bytes in the related data file and flush the data file header to disk.

┌─────────────────────────────────────────────────────────────────────────────┐
│The files must have been opened with the appropriate share attribute and not │
│in compatibility mode. SHARE.EXE MUST be installed or DOS error 1 is issued. │
└─────────────────────────────────────────────────────────────────────────────┘
This routine is transaction-based and will unlock all index files specified in
AccessPack and the data file. If an unlock fails the routine exits with a
return value indicating which access pack failed. This value is used as the
index into the AccessPack group for you to identify the error code. Note that
this routine does not attempt to re-lock those files unlocked successfully if
an error occurs in the transaction. If an error does occur (unlikely) you will
need to provide for unlocking the remaining files manually with the UnlockKeyXB
and UnlockDataXB routines. You should not rely on the operating system to
automatically unlock files when they're closed.

This routine is a combination of UnlockKeyXB and UnlockDataXB.

See: LockKeyXB  UnlockKeyXB  UnlockDataXB

~LockKeyXB

Pack: AccessPack        Src: LockKeyXBsrc       Func:  82/Network

Lock all bytes in the index file handle(s) for exclusive use by the current
process and reload the index file header(s) from disk.

┌─────────────────────────────────────────────────────────────────────────────┐
│The files must have been opened with the appropriate share attribute and not │
│in compatibility mode. SHARE.EXE MUST be installed or DOS error 1 is issued. │
└─────────────────────────────────────────────────────────────────────────────┘
This routine is transaction-based and will lock all index files specified in
AccessPack. If any lock fails, all previous locks by this routine are released.
The return value indicates which access pack failed, if any. This value is used
as the index into the AccessPack group for you to identify the error code.

The advantage of using region locks (LockKeyXB locks the entire file region) to
control file access is that the file does not need to be opened/closed using
the Deny Read/Write sharing attribute. Opening the file for Deny None, and
controlling subsequent access with region locks, allows for faster processing
since files do not need to be constantly opened and closed, as they would if
access were controlled by opening with Deny Read/Write.


See: UnlockKeyXB  LockXB

~UnlockKeyXB

Pack: AccessPack        Src: UnlockKeyXBsrc     Func:  83/Network

Unlock all bytes in the specified file handle(s) (previously locked) and flush
the file header(s) to disk (flush done before lock(s) released).

┌─────────────────────────────────────────────────────────────────────────────┐
│The files must have been opened with the appropriate share attribute and not │
│in compatibility mode. SHARE.EXE MUST be installed or DOS error 1 is issued. │
└─────────────────────────────────────────────────────────────────────────────┘
This routine is transaction-based and will unlock all index files specified in
AccessPack. If an unlock fails the routine exits with a return value indicating
which access pack failed. This value is used as the index into the AccessPack
group for you to identify the error code.

┌─────────────────────────────────────────────────────────────────────────────┐
│All file locks should be released when exclusive access in no longer needed. │
└─────────────────────────────────────────────────────────────────────────────┘
It is not recommended that you end your program without having released active
file locks. This is especially a valid concern for DOS versions prior to 5.0.
DOS 5 releases locks on files that are closed.


See: LockDataXB  UnlockXB

~LockDataXB

Pack: AccessPack        Src: LockDataXBsrc      Func:  84/Network

Lock all bytes in the file handle's data file for exclusive use by the current
process and reload the data file header from disk. You must set AP.RecNo=0 to
do this. To lock a single record, set AP.RecNo=record# to lock.

┌─────────────────────────────────────────────────────────────────────────────┐
│The files must have been opened with the appropriate share attribute and not │
│in compatibility mode. SHARE.EXE MUST be installed or DOS error 1 is issued. │
└─────────────────────────────────────────────────────────────────────────────┘
This routine locks the specified data file. If the handle specified is that of
an index file, that index file's related data file handle is used. For single-
record locks, AP.Handle must have a data file handle specified. Header loading
is not performed if locking a single record.

The advantage of using region locks (LockDataXB locks the entire file region)
to control file access is that the file does not need to be opened/closed using
the Deny Read/Write sharing attribute. Opening the file for Deny None, and
controlling subsequent access with region locks, allows for faster processing
since files do not need to be constantly opened and closed, as they would if
access were controlled by opening with Deny Read/Write.

See: UnlockDataXB

~UnlockDataXB

Pack: AccessPack        Src: UnlockDataXBsrc    Func:  85/Network

Unlock all bytes in the specified file handle (previously locked) and flush the
data file header to disk (flush done before lock released). To do this you must
set AP.RecNo=0. To unlock a single record, set AP.RecNo=record# to unlock.

┌─────────────────────────────────────────────────────────────────────────────┐
│The files must have been opened with the appropriate share attribute and not │
│in compatibility mode. SHARE.EXE MUST be installed or DOS error 1 is issued. │
└─────────────────────────────────────────────────────────────────────────────┘
This routine unlocks the specified data file. If the handle specified is that
of an index file that index file's related datafile handle is used. For single-
record unlocks, AP.Handle must have a data file handle specified. Flushing is
not performed if unlocking a single record.

┌─────────────────────────────────────────────────────────────────────────────┐
│All file locks should be released when exclusive access in no longer needed. │
└─────────────────────────────────────────────────────────────────────────────┘
It is not recommended that you end your program without having released active
file locks. This is especially a valid concern for DOS versions prior to 5.0.
DOS 5 releases locks on files that are closed.

See: DriveRemoteXB

~DriveRemoteXB

Pack: RemotePack        Src: DriveRemoteXBsrc   Func:  86/Network

Determine if specified drive is remote (default drive=0, A:=1, B=2, C=3...).

This routine uses INT21/44/sub function 09.

In addition to returning the IsRemote state, this routine sends back the result
of the DX register and also the install state of SHARE.EXE.

The meaning of the bitflags in Flags are (where IsRemote=0):

Bit   Meaning drive...
 1   1=uses 32-bit sectoring
 6   1=accepts Generic IOCTL (for INT21/44/0D,0E,0Fh)
 7   1=accepts Query IOCTL Device (INT21/44/11h)
 9   1=is local but shared by other computers in the network
11   1=accepts Does-Device-Use-Removable-Media (INT21/44/08)
13   1=requires media descriptor in FAT
14   1=accepts Receive/Send Control Data from Block Device (INT21/44/04,05)
15   1=is Substitution drive (set by the DOS SUBST command)
     (all other bits=0)

See: FileRemoteXB  LockXB

~FileRemoteXB

Pack: RemotePack        Src: FileRemoteXBsrc    Func:  87/Network

Determine if specified handle of file or device is remote.
 
This routine uses INT21/44/sub function 0Ah.

In addition to returning the IsRemote state, this routine sends back the result
of the DX register and also the install state of SHARE.EXE.

Flags bit 7=1 then handle is device, =0 then handle is file.
 
Bit   Meaning DEVICE...                 Bit   Meaning DEVICE...(cont)
 0   1=is console input device          13   1=is named pipe
 1   1=is console output device         15   1=is remote, 0=is local
 2   1=is null device                        (all other bits=0)
 3   1=is clock device                  Bit   Meaning FILE...
 4   1=is special device               0-5   xxxxxx=drive number (0=A...)
 5   1=is in binary mode, 0=in ASCII     6   1=has not been written to
 6   0=returns EOF if device is read    12   1=is NoInherit
11   1=is network spooler               14   1=date/time not set at close
12   1=is NoInherit                     15   1=is remote, 0=is local
                                             (all other bits=0)
See: SetRetriesXB  DriveRemoteXB  LockXB

~SetRetriesXB

Pack: SetRetriesPack    Src: SetRetriesXBsrc    Func:  88/Network

Set the number of times DOS retries disk operations after a failure due to
file-sharing operations (locked file regions from LockXB routines).

This routine uses INT21/44/sub function 0Bh.

By default DOS retries an operation 3 times (without pausing between attempts)
before returning an error to the application.

┌─────────────────────────────────────────────────────────────────────────────┐
│If you change the default values it's recommended that the default state be  │
│restored before your application ends (Retries=3, Pause=1).                  │
└─────────────────────────────────────────────────────────────────────────────┘
These values are pretty much determined by trial-and-error. You may find that
adding a delay between retries returns fewer access-denied errors.






See: DeleteFileDOS  LockXB

~DeleteFileDOS

Pack: DOSFilePack       Src: DeleteFileDOSsrc   Func: 100/DOS

Delete the specified file.

This routine uses DOS INT21/41 (interrupt 21h function 41h).

















See: RenameFileDOS

~RenameFileDOS

Pack: DOSFilePack       Src: RenameFileDOSsrc   Func: 101/DOS

Rename a file. May also be used to move the file to a new directory within the
partition.

This routine uses DOS INT21/56.

┌─────────────────────────────────────────────────────────────────────────────┐
│If the specified directory differs from the file's directory, the file's     │
│directory entry is moved to the new directory.                               │
└─────────────────────────────────────────────────────────────────────────────┘
For example, if the FilenamePtr filename is C:\LP100\PROJ93A.INF and the
NewFilenamePtr filename is C:\ARCH\PROJ93A.INA, the file is essentially
renamed and also moved to the \ARCH directory.








See: CreateFileDOS

~CreateFileDOS

Pack: DOSFilePack       Src: CreateFileDOSsrc   Func: 102/DOS

Create a new file.

This routine uses INT21/3C.

The specified filename/pathname must NOT already exist.

The file created is not left open. You must OpenFileDOS to use it.

The attribute used during the create can be:

  ATTRIBUTE       VALUE    MEANING
   Normal              0    normal access permitted to file
   Read-Only           1    read-only access permitted to file
   Hidden              2    file does not appear in directory listing
   System              4    file is a system file
   Volume              8    FILENAME used as volume label if no current label
   Archive            20h   file is marked for archiving



See: AccessFileDOS  OpenFileDOS

~AccessFileDOS

Pack: DOSFilePack       Src: AccessFileDOSsrc   Func: 103/DOS

Determine if the specified file can be accessed with the specified
access/sharing mode.

This routine uses INT21/3D and INT21/3E.

Basically, a Does-File-Exist routine. It uses the specified access/sharing
attributes when trying to open the file. For example, if you specify
DFP.Attr = &H42 (R/W access + Deny None sharing) and use AccessFileDOS on a
Read-Only DOS file, the return value would be DOS error 5, Access Denied.











See: OpenFileDOS

~OpenFileDOS

Pack: DOSFilePack       Src: OpenFileDOSsrc     Func: 104/DOS

Open the specified file with the specified access/sharing mode.

This routine uses INT21/3D.

  ACCESS          VALUE    MEANING
   Read-only           0    open for read-only access
   Write-only          1    open for write-only access
   Read/Write          2    open for read/write access
  SHARE
   Compatibility       0     any process may share file (not recommended)
   Deny Read/Write    10h    no other process may share file
   Deny Write         20h    no other process may share file for write
   Deny Read          30h    no other process may share file for read
   Deny None          40h    any process may share file except in Compatibilty
  INHERIT                                                                 mode
   NoInheritFlag      80h    if set child processes do not inherit file handles
                             (child process cannot inherit handle > 20)

The file access mode is a combination of ACCESS + SHARE + INHERIT.

See: SeekFileDOS  OpenPack

~SeekFileDOS

Pack: DOSFilePack       Src: SeekFileDOSsrc     Func: 105/DOS

Position the DOS file pointer of the specified file to the specified position.

This routine uses INT21/42.

The position is a 32-bit value and is relative to either the start of the file,
the current file pointer position, or the end of the file.

 Method  Meaning
    0    start move from the start of file (offset is a 32-bit unsigned value)
    1    start move at the current position (offset a signed value)
    2    start move at the end of file (offset a signed value)

For example, to move to the 511th byte of a file (byte 0 being the first), set
the offset value to 511 and use Method 0. On return, the absolute offset value
of the new position is returned. This is useful with Method 2 since you can
specify an offset of 0 and have the file length returned.
┌─────────────────────────────────────────────────────────────────────────────┐
│Never position the file pointer to before the start of file.                 │
└─────────────────────────────────────────────────────────────────────────────┘

See: ReadFileDOS

~ReadFileDOS

Pack: DOSFilePack       Src: ReadFileDOSsrc     Func: 106/DOS

Read from the file or device the specified number of bytes into a buffer.

This routine uses INT21/3F.

On block devices (such as disks) input starts at the current file position and
the file pointer is repositioned to the last byte read +1.

┌─────────────────────────────────────────────────────────────────────────────┐
│It is possible to read less than the bytes specified without an error being  │
│generated. Compare the bytes to read with the returned bytes read value. If  │
│less then end of file was reached during the read, if 0 then file was at EOF.│
└─────────────────────────────────────────────────────────────────────────────┘
By using DOS's predefined handles you can read from the keyboard (STDIN) by
using the STDIN handle, 0. The input will terminate after all specified bytes
have been read or after a CR (ASCII 0Dh). If more bytes are entered than were
requested, the next read will retrieve those excess bytes. Therefore, it's
suggested that you specify 129 bytes to input (DOS will process 127+CR/LF bytes
maximum when reading the STDIN device). Post-process the entered data by
scanning for the CR/LF.

See: ExpandFileDOS

~ExpandFileDOS

Pack: DOSFilePack       Src: ExpandFileDOSsrc   Func: 107/DOS

Expands the specified file by the specified number of bytes.

This routine uses INT21/42 and INT21/40.

This routine is useful in pre-allocating disk space. By reserving disk space in
advance you can guarantee that enough disk space will be available for a future
operation (especially if more than 1 process is running). You'll also be able
ensure that the disk space that a file does use is as contiguous as possible.

Database systems are dynamic and their files typically allocate new space on
an as-needed basis. This dynamic allocation can cause parts of a file to be
located throughout the disk system, possibly affecting performance drastically.
By pre-allocating the disk space you can be assured of consistent throughput
performance since the file is contiguous.






See: WriteFileDOS

~WriteFileDOS

Pack: DOSFilePack       Src: WriteFileDOSsrc    Func: 108/DOS

Write to the file or device the specified number of bytes from a buffer.

This routine uses INT21/40.

┌─────────────────────────────────────────────────────────────────────────────┐
│If the number of bytes written is less than the specified bytes, this routine│
│returns a -2 error code (or 65554 unsigned).                                 │
└─────────────────────────────────────────────────────────────────────────────┘
On block devices (such as disk) output starts at the current file position, and
the file pointer is repositioned to the last byte written +1.

┌─────────────────────────────────────────────────────────────────────────────┐
│If the specified bytes to write is 0, the file is truncated at the current   │
│file pointer position.                                                       │
└─────────────────────────────────────────────────────────────────────────────┘
By using DOS's predefined handles you can write to the screen (STDOUT) by
using the STDOUT handle, 1.



See: CloseFileDOS

~CloseFileDOS

Pack: DOSFilePack       Src: CloseFileDOSsrc    Func: 109/DOS

Close the file flushing any internal buffers, releasing any locked regions, and
update the directory entry to the correct size, date, and time.

This routine uses INT21/3E.

If you have opened a file using the DOS open routine you should close it when
you no longer need it.

┌─────────────────────────────────────────────────────────────────────────────┐
│This routine can be used to close the predefined DOS handles (0-4) and make  │
│those handles available for reuse. Typically handles 0 and 1 should not be   │
│closed by an application since they are the STDIN and STDOUT that DOS uses   │
│for the current application (keyboard and screen).                           │
└─────────────────────────────────────────────────────────────────────────────┘
Since BULLET provides for up to 250 user file handles for your applications it
isn't necessary for you to eek 3 more file handles by closing handles 2-4.




See: MakeDirDOS

~MakeDirDOS

Pack: DOSFilePack       Src: MakeDirDOSsrc      Func: 110/DOS

Create a new subdirectory.

This routine uses INT21/39.

















See: DeleteFileDOS

~AccessPack

 Src: InsertXBsrc      Func: InsertXB and many more

TYPE AccessPack        'AP (AP is recommended pack name for DIM, AP.Func=)
Func AS INTEGER        'varies
Stat AS INTEGER        'ret:completion status
Handle AS INTEGER      'OS handle
RecNo AS LONG          ' in:rec number to get/delete/update (if applicable)
                       ' in:set to single rec# to lock or set to 0 to lock all
                       'ret:record number of data record accessed
RecPtrOff AS INTEGER   'far pointer to record storage buffer
RecPtrSeg AS INTEGER
KeyPtrOff AS INTEGER   'far pointer to search key buffer
KeyPtrSeg AS INTEGER
NextPtrOff AS INTEGER  'far pointer to next key access pack
NextPtrSeg AS INTEGER  'or 0:0 if end of link or if N/A
END TYPE  '22

The NextPtr variables are only used by InsertXB, UpdateXB, ReindexXB, and the
LockXB routines. NextPtr is used as a link to the next related access pack,
if any. Not all entries are used by all routines. Generally, any routine that
gets/puts user data to the database uses this pack.

See: BreakPack

~BreakPack

 Src: BreakXBsrc       Func: BreakXB

TYPE BreakPack             'BP
Func AS INTEGER            '4
Stat AS INTEGER            'ret:completion status
Mode AS INTEGER            '=0 disable Ctrl-C/Ctrl-Break, 1=restore
END TYPE '6 bytes

A simple pack.













See: CopyPack

~CopyPack

 Src: BackupFileXBsrc  Func: BackupFileXB, CopyDHXB, CopyKHXB

TYPE CopyPack              'CP
Func AS INTEGER            '5=BackupFileXB,16=CopyDHXB,26=CopyKHXB
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'handle of BULLET file
FilenamePtrOff AS INTEGER  'far pointer to filenameZ
FilenamePtrSeg AS INTEGER  '(filename must end with a CHR$(0)))
END TYPE '10













See: CreateDataPack

~CreateDataPack

 Src: CreateDXBsrc     Func: CreateDXB

TYPE CreateDataPackTYPE    'CDP
Func AS INTEGER            '10
Stat AS INTEGER            'ret:completion status
FilenamePtrOff AS INTEGER  'far pointer to filenameZ to create
FilenamePtrSeg AS INTEGER  '(filename must end with a CHR$(0))
NoFields AS INTEGER        'number of fields per record
FieldListPtrOff AS INTEGER 'far pointer to field list
FieldListPtrSeg AS INTEGER '(field list is of type FieldDescTYPE )
FileID AS INTEGER          'file signature byte, usually=3
END TYPE '16

The filename pointed at by the FilenamePtr variables should be a fixed-length
string (e.g., FILEN AS STRING * 80) so that VARSEG/VARPTR can be used to get
its memory address. The filename must end with a CHR$(0) immediately following
the last character of the filename: FILEN = YourFilename$ + CHR$(0).

The FieldListPtr variables point to an array of type FieldDescTYPE. This array
is dimensioned for as many fields as there are in the record and contains the
field descriptors, one for each field.

See: CreateKeyPack  FieldDescTYPE

~CreateKeyPack

 Src: CreateKXBsrc     Func: CreateKXB

TYPE CreateKeyPack         'CKP
Func AS INTEGER            '20
Stat AS INTEGER            'ret:completion status
FilenamePtrOff AS INTEGER  'far pointer to filenameZ
FilenamePtrSeg AS INTEGER  '(filename must end with a CHR$(0))
KeyExpPtrOff AS INTEGER    'far pointer to key expressionZ
KeyExpPtrSeg AS INTEGER    '(key expression must end with a CHR$(0))
XBlink AS INTEGER          'BULLET XB data file handle this key file indexes
KeyFlags AS INTEGER        'bit 0=unique,1=char,4=int,5=long,(E=NLS),F=signed
CodePageID AS INTEGER      'code page ID for NLS, -1 to use system default
CountryCode AS INTEGER     'country code number for NLS, -1 to use default
CollatePtrOff AS INTEGER   'far pointer to programmer-supplied collate table
CollatePtrSeg AS INTEGER   'or 0:0 if using system-determined NLS table
END TYPE '24

Bit 14 in KeyFlags (0Eh) is set by BULLET during CreateKXB if a collate table
is present.



See: DescriptorPack  is_NLS

~DescriptorPack

 Src: GetDescriptorXBsrc  Func: GetDescriptorXB

TYPE DescriptorPack        'DP
Func AS INTEGER            '30
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'BULLET data file handle to get information on
FieldNumber AS INTEGER     'field number to get info on, or if 0 then...
FD AS FieldDescTYPE        '...search for DP.FD.FieldName
END TYPE '40

GetDescriptorXB allows you to get the field descriptor info for a particular
field number (as in the first field, or the 10th field, etc.) or, if you don't
know the physical field number, the routine can also get the info for a field
by field name.

To get the info for field number, say 5, set DP.FieldNumber = 5. The DP.FD
structure element is filled in with field 5's information.

To get the info for a field by fieldname, say LASTNAME, set DP.FieldNumber=0 &
DP.FD.FieldName = "LASTNAME" + STRING$(11,0)--the fieldname must be zero-filled
and zero-terminated--adding 11 ASCII zeroes ensures this requirement.

See: DOSFilePack  FieldDescTYPE

~DOSFilePack

 Src: AccessFileDOSsrc  Func: AccessFileDOS
                              (all routines ending with DOS)
TYPE DOSFilePack           'DFP
Func AS INTEGER            'varies, see DeleteFileDOS for first of DOS routines
Stat AS INTEGER            'ret:completion status
FilenamePtrOff AS INTEGER  'far pointer to filenameZ
FilenamePtrSeg AS INTEGER  '(filename must end with a CHR$(0))
Handle AS INTEGER          'in: handle to access  ret: handle opened
ASmode AS INTEGER          'open access/sharing mode
Bytes AS INTEGER           'in: bytes to read  ret: bytes read
SeekOffset AS LONG         'seek to file position
Method AS INTEGER          'seek method
BufferPtrOff AS INTEGER    'far pointer to read/write buffer
BufferPtrSeg AS INTEGER
Attr AS INTEGER            'file create directory entry attribute
NewNamePtrOff AS INTEGER   'far pointer to new filenameZ for rename
NewNamePtrSeg AS INTEGER   '(filename must end with a CHR$(0))
END TYPE '30

All of the xDOS routines use this pack. Often only a few of the structure
elements are used by any one of the routines. Set only those needed.

See: DVmonPack

~DVmonPack

 Src: DVmonCXBsrc      Func: DVmonCXB

TYPE DVmonPackTYPE         'THIS ROUTINE IS AVAILABLE ONLY IN THE DEBUG ENGINE
Func AS INTEGER            '9
Stat AS INTEGER            'ret:completion status
Mode AS INTEGER            '=0 disable montitoring, =1 enable
Handle AS INTEGER          'file handle to monitor
VideoSeg AS INTEGER        'segment to write screen image (e.g., &HB800)
END TYPE '10 bytes

This routine is supplied only in the BULLET debug engine. It displays real-time
monitoring information of a .DBF file or index and .DBF file pair including
searches, seeks, hits, current record number, current key, key node contents,
key node pointers, stack state, key and record counts, and other info.

By using the HereSeg value returned from StatKXB you can locate the searches,
seeks, and hits data at (DEF SEG=HereSeg: Seeks&=GetLongAt&(517): DEF SEG)

 +513 Searches AS LONG ;keys searched for since open
 +517 Seeks AS LONG    ;nodes seeked since open
 +521 Hits AS LONG     ;seeks satisfied without disk access

See: ExitPack

~ExitPack

 Src: InitXBsrc        Func: ExitXB, AtExitXB

TYPE ExitPack              'EP
Func AS INTEGER            '1=ExitXB, 2=AtExitXB
Stat AS INTEGER            'ret:completion status
END TYPE '4 bytes
















See: FieldDescTYPE

~FieldDescTYPE

 Src: CreateDXBsrc     Func: CreateDXB

TYPE FieldDescTYPE         'this TYPE is used by CreateDataPack ONLY
FieldName AS STRING * 11   'zero-filled field name (use only ASCII 65-90,95)
FieldType AS STRING * 1    'C-har,N-umeric,D-ate,L-ogical,M-emo
FieldDA AS LONG            '=0,reserved
FieldLength AS STRING * 1  'C=1-254,N=1-19(varies),D=8,L=1,M=10
FieldDC AS STRING * 1      'decimal places for FieldType=N (0,2-15)
A1 AS INTEGER              '=0,reserved
A2 AS INTEGER              '=0,reserved
filler AS STRING * 10      '=0,reserved
END TYPE '32

If you can can forgo dBASE compatility you can use the B field type. This type
is for fields that contain binary data (all dBASE fields contain ASCII text or
numeric strings). If you specify a FieldType = "B" for, say an integer field,
use a FieldLen = 2. If the field is a long integer, use FieldLen = 4. You can
also use this non-standard field type for indexing. See CreateKXB for more.




See: HandlePack  CreateDataPack  CreateKXB

~HandlePack

 Src: CloseDXBsrc      Func: CloseDXB, ReadDHXB, FlushDHXB, ZapDHXB
                             CloseKXB, ReadKHXB, FlushKHXB, ZapKHXB
                           'HP
TYPE HandlePack            '12=CloseDXB,14=ReadDHXB,15=FlushDHXB,17=ZapDHXB
Func AS INTEGER            '22=CloseKXB,24=ReadKHXB,25=FlushKHXB,27=ZapKHXB
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'handle of BULLET file
END TYPE '6














See: InitPack

~InitPack

 Src: InitXBsrc        Func: InitXB

TYPE InitPack              'IP
Func AS INTEGER            '0
Stat AS INTEGER            'ret:completion status
JFTmode AS INTEGER         'expand JFT if non-zero
DOSver AS INTEGER          'ret:DOS version
Version AS INTEGER         'ret:BULLET version * 100
ExitOff AS INTEGER         'ret:far pointer to ExitXB routine, offset
ExitSeg AS INTEGER         'ret:segment of ExitXB
END TYPE '12 bytes









     

See: MemoryPack

~MemoryPack

 Src: MemoryXBsrc      Func: MemoryXB

TYPE MemoryPack            'MP
Func AS INTEGER            '3
Stat AS INTEGER            'ret:completion status
Memory AS LONG             'ret:largest free OS memory block
END TYPE '8 bytes















See: OpenPack

~OpenPack

 Src: OpenDXBsrc       Func: OpenDXB, OpenKXB

TYPE OpenPack              'OP
Func AS INTEGER            '11=OpenDXB,21=OpenKXB
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'ret:OS handle of file opened
FilenamePtrOff AS INTEGER  'far pointer to filenameZ to open
FilenamePtrSeg AS INTEGER  '(filename must end with a CHR$(0))
ASmode AS INTEGER          'DOS access-sharing mode (see OpenFileDOS)
xbHandle AS INTEGER        'if opening key file this is its related data file
END TYPE '14               '(if opening data file xbHandle is not used)

                           'Note: you must supply xbHandle on index file opens









See: RemotePack  OpenFileDOS

~RemotePack

 Src: DriveRemoteXBsrc  Func: DriveRemoteXB, FileRemoteXB

TYPE RemotePack            'RP
Func AS INTEGER            '86=DriveRemoteXB,87=FileRemoteXB
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'handle/drive depending on routine
IsRemote AS INTEGER        'ret:0=local,1=remote
Flags AS INTEGER           'ret:dx register as returned by DOS
IsShare AS INTEGER         'ret:0=SHARE.EXE not loaded
END TYPE '12












See: SetRetriesPack

~SetRetriesPack

 Src: SetRetriesXBsrc  Func: SetRetriesXB

TYPE SetRetriesPack        'SRP
Func AS INTEGER            '88
Stat AS INTEGER            'ret:completion status
Mode AS INTEGER            '0=set DOS default else use Pauses/Retries below
Pause AS INTEGER           '0-65535 loop counter between retries
Retries As INTEGER         '0-65535 retries to access locked file
END TYPE '10

The default values for Retries is 3 and Pause is 1.

The Pause value is used as a simple loop counter used to waste time. This loop
IS dependent on CPU power so values are not portable across different machines.

Do not use unrealistic values. For example, don't set Retries to 30,000 unless
you really want to wait for DOS to try 30,000 times before returning an error!





See: StatDataPack

~StatDataPack

 Src: StatDXBsrc       Func: StatDXB

TYPE StatDataPackTYPE      'SDP
Func AS INTEGER            '13
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'BULLET data file to get status on
FileType AS STRING * 1     'ret:1=BULLET XB data file
Dirty AS STRING * 1        'ret:0=not changed
Recs AS LONG               'ret:records in file
RecLen AS INTEGER          'ret:record length
Fields AS INTEGER          'ret:fields per record ()
f1 AS STRING * 1           'reserved (1=update DVmon)
LUyear AS STRING * 1       'ret:binary, year file last updated
LUmonth AS STRING * 1      'ret:month   --LUs are 0 if DBF newly created
LUday AS STRING * 1        'ret:day
HereSeg AS INTEGER         'ret:this file's control segment
filler AS STRING * 10      'reserved
END TYPE '32




See: StatKeyPack

~StatKeyPack

 Src: StatKXBsrc       Func: StatKXB

TYPE StatKeyPack           'SKP
Func AS INTEGER            '23
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'BULLET key file to get status on
FileType AS STRING * 1     'ret:0=BULLET XB key file
Dirty AS STRING * 1        'ret:0=not changed
Keys AS LONG               'ret:keys in file
KeyLen AS INTEGER          'ret:key length
XBlink AS INTEGER          'ret:related BULLET XB data file handle
XBrecno AS LONG            'ret:record number attached to current key
HereSeg AS INTEGER         'ret:this file's control segment
CodePageID AS INTEGER      'ret:code page ID number of key file sort
CountryCode AS INTEGER     'ret:country code number of key file sort
CollateTableSize AS INTEGER 'ret: size of collate table, 0 or 256
KeyFlags AS INTEGER        'ret:bit 0=unique,1=char,4=int,5=long,E=NLS,F=signed
filler AS STRING * 2
END TYPE '32



See: StatHandlePack

~StatHandlePack

 Src: StatHandleXBsrc  Func: StatHandleXB

TYPE StatHandlePack        'SHP
Func AS INTEGER            '6
Stat AS INTEGER            'ret:completion status
Handle AS INTEGER          'file handle to get information on
ID AS INTEGER              'ret:0=XB index,1=XB data file,-1=not BULLET handle
END TYPE '8 bytes














See: XErrorPack

~XErrorPack

 Src: GetExtErrorXBsrc Func: GetExtErrorXB

TYPE XErrorPack            'XEP
Func AS INTEGER            '7
Stat AS INTEGER            'ret:extended error
Class AS INTEGER           'ret:error class
Action AS INTEGER          'ret:suggested action
Location AS INTEGER        'ret:error location
END TYPE '10 bytes













See: AccessPack  Errors_DOS

~Errors_BULLET    (200-209)

200 key not found - The search key for Equal was not matched exactly.
    Next/Prev routines can be used to continue search from point of mismatch.

201 key already exists - Attempted to add a key that already exists in the
    index file created to allow only unique keys.

202 end of file - A Next routine is past the last key of the index file.

203 top of file - A Prev routine is before the first key of the index file.

204 key file empty - A key access was attempted with no keys in the index file.

205 key type unknown - Generally indicates a corrupt index header (keyflags
    unknown at key insert).

    reserved,206-207

208 no more nodes - The index file has reached full capacity (32MB). ReindexXB
    can often shrink an index file by 30 to 50%.

209 key file corrupt - The index file is corrupt (write attempt to node 0).

See: Errors_BULLET_b

~Errors_BULLET_b  (210-232)

210 key file corrupt - The index file is corrupt (internal overflow).

    reserved,211-219

220 incorrect DOS version - BULLET requires DOS 3.3 or later.

221 invalid key length - The key is > 62 bytes (or 64 if unique specified).

222 file not open - The specified handle is not an open BULLET file.
       
    reserved,223

224 invalid record number - The specified record number is < 0, past the last
    record number in the .DBF, or is > 16,777,215.

    reserved,225-227

228 invalid filetype - The specified handle is not the correct type for the
    operation (i.e., specifying a data file handle for a key file operation).

    reserved,229-232

See: Errors_BULLET_c

~Errors_BULLET_c  (233-243)

233 init not active - InitXB must be called before all others except MemoryXB.

234 init already active - InitXB has already been called. Use ExitXB first to
    call InitXB more than once per process. (Make sure the xxP.Func <> 0.)

235 too many indexes - BULLET can handle up to 32 index files per transaction
    record with the InsertXB and UpdateXB routines. Contact the author if you
    need to allow for more than 32 index files/transaction record.

    reserved,236-239

240 invalid key expression - The CreateKXB key expression could not be
    evaluated.

    reserved,241

242 field not found - The fieldname was not found in the descriptor area.

243 invalid field count - Too many fields were specified or the specified field
    number is past the last field.


See: Errors_BULLET_d

~Errors_BULLET_d  (244-255)

    reserved,244-249

250 invalid country info - The specifed country code or code page ID is not
    valid or not installed (according to DOS).

251 invalid collate table size - The specified country code/code page ID uses
    a collate-sequence table > 256 bytes (2-byte characters as with Kanji).

252 invalid keyflags - The specified keyflags are invalid.

    reserved,253-254

255 evaluation mode shutdown - BULLET evaluation period has completed.
    You can reinstall to continue evaluation, though you may want to consider
    your motives for reinstalling since the original evaluation period has
    expired. This error occurs only after the evaluation period has expired.
    It is not recommended that you continue to use BULLET after the evaluation
    period. It is possible for no 255 error to be generated for quite some
    time since it occurs only under certain load conditions and then only when
    certain routine sequences are performed. The specified evaluation period of
    21 days should be adhered to.

See: Errors_BASIC

~Errors_BASIC

 1 NEXT without FOR                    24 device timeout
 2 syntax error                        25 device fault
 3 RETURN without GOSUB                26 FOR without NEXT
 4 out of DATA                         27 out of paper
 5 illegal function call               29 WHILE without WEND
 6 overflow                            30 WEND without WHILE
 7 out of memory                          reserved,31-32
 8 label not defined                   33 duplicate label
 9 subscript out of range                 reserved,34
10 duplicate definition                35 subprogram not defined
11 division by zero                       reserved,36
12 illegal in direct mode              37 argument-count mismatch
13 type mismatch                       38 array not defined
14 out of string space                    reserved,39
   reserved,15                         40 variable required
16 string formula too complex             reserved,41-49
17 cannot continue                     50 FIELD overflow
18 function not defined                51 internal error
19 no RESUME                           52 bad file name or number
20 RESUME without error                53 file not found
   reserved,21-23                      54 bad file mode

See: Errors_BASIC_b

~Errors_BASIC_b

55 file already open                      reserved,77-79
56 FIELD statement active              80 feature removed
57 device I/O error                    81 invalid name
58 file already exists                 82 table not found
59 bad record length                   83 index not found
   reserved,60                         84 invalid column
61 disk full                           85 no current record
62 input past end of file              86 duplicate value for unique index
63 bad record number                   87 invalid operation on null index
64 bad file name                       88 database needs repair
   reserved,65-66                      89 insufficient ISAM buffers
67 too many files
68 device unavailable
69 communication-buffer overflow
70 permission denied
71 disk not ready
72 disk-media error
73 feature unavailable
74 rename across disks
75 path/File access error
76 path not found

See: Errors_DOS

~Errors_DOS

-2 disk full or unexpected end of file
-1 bad filename
 0 no error
 1 function not supported              19 disk write protected
 2 file not found                      20 unknown unit
 3 path not found                      21 drive not ready
 4 too many open files                 22 unknown command
 5 access denied (see Specs_Networks)  23 data error (CRC)
 6 handle invalid                      24 bad request structure length
 7 MCBs destroyed                      25 seek error
 8 not enough memory                   26 unknown medium type
 9 memory block address invalid        27 sector not found
10 environment invalid                 28 printer out of paper
11 format invalid                      29 write fault
12 access code invalid                 30 read fault
13 data invalid                        31 general failure
   reserved-0Eh                        32 sharing violation
15 disk drive invalid                  33 lock violation
16 cannot remove current directory     34 disk change invalid/wrong disk
17 not same device                     35 FCB unavailable
18 no more files                       36 sharing buffer overflow

See: Errors_DOS_b

~Errors_DOS_b

37 code page mismatched                58 incorrect response from network
38 handle EOF                          59 unexpected network error
39 handle disk full                    60 incompatible remote adapter
   reserved-28h                        61 print queue full
   reserved-29h                        62 no spool space
   reserved-2Ah                        63 not enough space to print file
   reserved-2Bh                        64 network name deleted
   reserved-2Ch                        65 network access denied
   reserved-2Dh                        66 network device type incorrect
   reserved-2Eh                        67 network name not found
   reserved-2Fh                        68 network name limit exceeded
   reserved-30h                        69 NETBIOS session limit exceeded
   reserved-31h                        70 sharing temporarily paused
50 network request not supported       71 network request not accepted
51 remote computer not listening       72 print/disk redirection paused
52 duplicate name on network              reserved-49h
53 network pathname not found             reserved-4Ah
54 network busy                           reserved-4Bh
55 network device no longer exists        reserved-4Ch
56 NETBIOS command limit exceeded         reserved-4Dh
57 network adapter hardware error         reserved-4Eh

See: Errors_DOS_c

~Errors_DOS_c

   reserved-4Fh                  ────────────── DOS Class Codes ──────────────
80 file exists
81 duplicate FCB                 1 out of resources       7 application error
82 cannot make                   2 temporary situation    8 not found
83 fail on INT24                 3 authorization          9 bad format
84 out of structures             4 internal error        10 locked
85 already assigned              5 hardware failure      11 media failure
86 invalid password              6 system failure        12 already exists
87 invalid parameter                                     13 unknown
88 network write fault
   reserved-59h                    DOS Action Codes         DOS Locus Codes
90 sys comp not loaded
                                 1 retry immediately      1 unknown
                                 2 delay and retry        2 block device
                                 3 reenter input          3 network
                                 4 abort ASAP             4 serial device
                                 5 abort immediately      5 memory
                                 6 ignore error
                                 7 user intervention



See: Errors_BULLET

~InitXBsrc

Func: InitXB           Pack: InitPack           Func:   0/System

DIM IP AS InitPack
DIM EP AS ExitPack

IP.Func = InitXB        'InitXB defined in BULLET.BI
IP.JFTmode = 1          'expand JFT to 255 handles
stat = BULLET(IP)
IF stat = 0 THEN
   DOSmajor = IP.DOSver\256
   DOSminor = IP.DOSver MOD 256
   BULLETver = IP.Version
   SegExitXB = IP.ExitSeg
   OffExitXB = IP.ExitOff
   EP.Func = AtExitXB   'register ExitXB with _atexit shutdown routine
   stat = BULLET(EP)
ENDIF
IF stat THEN 'error




See: ExitXBsrc

~ExitXBsrc

Func: ExitXB           Pack: ExitPack           Func:   1/System

DIM EP AS ExitPack
EP.Func = ExitXB        'ExitXB defined in BULLET.BI
stat = BULLET(EP)

The return value from ExitXB is currently always 0.















See: AtExitXBsrc

~AtExitXBsrc

Func: AtExitXB         Pack: ExitPack           Func:   2/System

DIM IP AS InitPack
DIM EP AS ExitPack

IP.Func = InitXB
IP.JFTmode = 1
stat = BULLET(IP)
IF stat = 0 THEN
   DOSmajor = IP.DOSver\256
   DOSminor = IP.DOSver MOD 256
   SegExitXB = IP.ExitSeg
   OffExitXB = IP.ExitOff
                        'register ExitXB with _atexit shutdown routine
   EP.Func = AtExitXB   'AtExitXB defined in BULLET.BI
   stat = BULLET(EP)    'the return value is that returned from the compiler
                        '_atexit routine, 0=okay, anything else is an error
ENDIF                   'indicating, for example, the _atexit register is full
IF stat THEN 'error



See: MemoryXBsrc

~MemoryXBsrc

Func: MemoryXB         Pack: MemoryPack         Func:   3/System

DIM MP AS MemoryPack

WorkSpace& = 50000      'a value at least 40K or so
MemoryNeeded& = WorkSpace& + (144&+ ((1+NF)*32)) * TotalOpenDataFiles _
                           + (1264& * TotalOpenKeyFiles)
MP.Func = MemoryXB
stat = BULLET(MP)
IF MP.Memory < MemoryNeeded& THEN
    QBheap& = SETMEM(-MemoryNeeded& + 4096)  'release what we need+QB fudge
    MP.Func = MemoryXB
    stat = BULLET(MP)
    IF MP.Memory < MemoryNeeded& THEN 'not enough memory
END IF

MP.Memory does not reflect memory available through DOS in the UMB area. It's
possible that all memory requests can be satisfied by UMB RAM. Consult a DOS 5+
programmer reference for more information on this (see DOS INT21/58 for more).

In the QuickBASIC/BASIC PDS environment do not call SETMEM(-x) more than once.

See: BreakXBsrc

~BreakXBsrc

Func: BreakXB          Pack: BreakPack          Func:   4/System

DIM BP AS BreakPack

BP.Func = BreakXB       'BreakXB defined in BULLET.BI
BP.Mode = 0             'disable Ctrl-C/Ctrl-BREAK (do nothing on those keys)
stat = BULLET(BP)       'stat=0 always

If BreakXB is called multiple times with the same BP.mode each time, only the
first is acted on. You can set BP.mode = 1 to restore the default handlers
(those installed originally) and then again set BP.Mode = 0 to disable them.

ExitXB calls this routine automatically as part of the BULLET shutdown to
restore the original default break handlers.








See: BackupFileXBsrc

~BackupFileXBsrc

Func: BackupFileXB     Pack: CopyPack           Func:   5/System

DIM AP AS AccessPack
DIM CP AS CopyPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()
                                'so same source QB AND BASIC7 /Fs compatible
TempStr = NewFilename$          'assign the var-len string to fixed-len str

CP.Func = BackupFileXB  'defined in BULLET.BI
CP.Handle = DataHandle  'handle of data file to backup
CP.FilenamePtrOff = VARPTR(TempStr) 'ALWAYS use VARPTR() on fixed-len string
CP.FilenamePtrSeg = VARSEG(TempStr) 'ALWAYS use VARSEG() on fixed-len string
stat = BULLET(CP)       'copy the data file to the file NewFilename$
IF stat = 0 THEN
   AP.Func = PackRecordsXB
   AP.Handle = DataHandle
   stat = BULLET(AP)
ENDIF
IF stat THEN 'error



See: StatHandleXBsrc

~StatHandleXBsrc

Func: StatHandleXB     Pack: StatHandlePack     Func:   6/System

DIM SHP AS StatHandlePack
DIM SKP AS StatKeyPack
DIM SDP AS StatDataPack

SHP.Func = StatHandleXB 'defined in BULLET.BI
SHP.Handle = TheHandleNumber
stat = BULLET(SHP)
IF SHP.ID = 0 THEN      'handle belongs to an index file (index file/key file)
   SKP.Func = StatKXB   'get key stats  -- see StatKXB/StatDXB for more
   SKP.Handle = PassedHandleNumber      ' on the SKP structure
   stat = BULLET(SKP)
ELSEIF SHP.ID = 1 THEN  '.DBF data file
   'get DBF stats
ELSE
   'error not a BULLET file type
ENDIF




See: GetExtErrorXBsrc

~GetExtErrorXBsrc

Func: GetExtErrorXB    Pack: XErrorPack         Func:   7/System

'an error just occured in the range 1 to 199 as returned in one of the
'pack.Stat variables (current max DOS error is 90 (5Ah))

'remember, transaction-based routines return a bad pack index in the return
'stat value, which you use to check the appropriate pack.Stat variable

DIM XEP AS XErrorPack

XEP.Func = GetExtErrorXB 'defined in BULLET.BI
stat = BULLET(XEP)
IF stat <> 0 THEN
   PRINT "Extended Codes --"
   PRINT "            error: "; XEP.Stat
   PRINT "      error class: "; XEP.Class
   PRINT "recommened action: "; XEP.Action
   PRINT "         location: "; XEP.Location
ELSE
   PRINT "No error"
ENDIF

See: DVmonCXBsrc  StatKXB

~DVmonCXBsrc

Func: DVmonCXB         Pack: DVmonPack          Func:   9/DEBUG

'at this point a data file and a key file have been opened
'kf is that key file's DOS handle

DIM DV AS DVmonPack

DV.Func = DVmonCXB      'defined in BULLET.BI
DV.Mode = 1             'enable monitoring
DV.Handle = kf          'monitor key file handle, kf (and its XBlink data file)
DV.VideoSeg = &HB800+(4096\16)  'output to color screen, page 1 (pages 0 to ?)
stat = BULLET(DV)       'stat=0 always even if not DEBUG ENGINE

For two-monitor systems (with a color monitor as the main system) output should
be directed to &HB000, the mono monitor's video memory.

DVmonCXB stands for Dual Video Monitor Control XB.





See: CreateDXBsrc

~CreateDXBsrc

Func: CreateDXB        Pack: CreateDataPack     Func:  10/Mid-level

DIM CDP AS CreateDataPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()

REDIM FD(1 TO 2) AS FieldDescTYPE 'field descriptions for each of the fields...
                                  '...in the record (record has 2 fields)

TempStr = filename$ + CHR$(0)   'assign filename to a fixed-len string...
                                '...so we can use VARSEG/VARPTR

'build FD first for each of the fields in the record
FD(1).FieldName = "STUDENT" + STRING$(10,0)   'must be zero-filled
FD(1).FieldType = "C"           'a character field type
FD(1).FieldLength = CHR$(20)    'up to 20 characters (a byte field--use CHR$())
FD(1).FieldDC = CHR$(0)         'non-numeric has no decimal places
FD(2).FieldName = "SCORE" + STRING$(10,0)
FD(2).FieldType = "N"
FD(2).FieldLength = CHR$(3)     'dBASE numeric format, allow for "100"
FD(2).FieldDC = CHR(0)          'no decimal places used for this field
'(cont)
                     'for BINARY FieldType="B" see FieldDescTYPE
See: CreateDXBsrc_a                                                     -MORE-

~CreateDXBsrc_a

'build the CDP
CDP.Func = CreateDXB            'defined in BULLET.BI
CDP.FilenamePtrOff = VARPTR(TempStr) 'point to filenameZ (Z=0-terminated str)
CDP.FilenamePtrSeg = VARSEG(TempStr)
CDP.NoFields = 2                'this example has 2 fields
CDP.FieldListPtrOff = VARPTR(FD(1)) 'point to the first field decription...
CDP.FieldListPtrSeg = VARPTR(FD(1)) '...defined in the previous screen
CDP.FileID = 3                  'standard dBASE file ID
stat = BULLET(CDP)              'create the DBF data file
IF stat THEN 'error

Normally this code would be written as a generalized FUNCTION. The CDP could be
a global allocation (DIM SHARED CDP AS CreateDataPack) and the FD() would also
be (REDIM [SHARED] FD(1 TO 1) AS FieldDescTYPE) and REDIM'd to needed size when
used. A possible header:

 DECLARE FUNCTION CreateDBF% (filename$, NoFields%, FD() AS FieldDescTYPE)

To create a DBF you'd need just the filename, number of fields, REDIM the FD()
array (if needed), fill in FD() array, then call the function. Look at the
source code examples on the distribution disk for more.

See: OpenDXBsrc  CreateDXBsrc

~OpenDXBsrc

Func: OpenDXB          Pack: OpenPack           Func:  11/Mid-level

DIM OP AS OpenPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()

TempStr = filename$ + CHR$(0)   'assign filename to a fixed-len string...

OP.Func = OpenDXB                   'defined in BULLET.BI
OP.FilenamePtrOff = VARPTR(TempStr) 'point to filenameZ (Z=0-terminated str)
OP.FilenamePtrSeg = VARSEG(TempStr)
OP.ASmode = ReadWrite + DenyNone    'defined in BULLET.BI
stat = BULLET(OP)
IF stat THEN 'error

The ASmode (access/sharing mode) determines how the operating system controls
access to the file. See OpenFileDOS for the meanings of the various ASmodes.






See: CloseDXBsrc  OpenFileDOS

~CloseDXBsrc

Func: CloseDXB         Pack: HandlePack         Func:  12/Mid-level

DIM HP AS HandlePack

HP.Func = CloseDXB              'defined in BULLET.BI
HP.Handle = datahandle          'handle of the file to close
stat = BULLET(HP)
IF stat THEN 'error














See: StatDXBsrc

~StatDXBsrc

Func: StatDXB          Pack: StatDataPack       Func:  13/Mid-level

DIM SDP AS StatDataPack

SDP.Func = StatDXB          'defined in BULLET.BI
SDP.Handle = datahandle     'data handle to get stats on
stat = BULLET(SDP)          'must be a data handle, use StatHandleXB if you...
IF stat = 0 THEN            '...don't know the type of file a handle's for
   'SDP.FileType is set to 1
   'SDP.Dirty is set to 1 if the file has changed (0=not changed)
   'SDP.Recs = number of records in the DBF file
   'SDP.RecLen = record length
   'SDP.Fields = number of fields in the record
   'SDP.f1 is reserved
   'SDP.LUyear = year file last updated, binary (year = ASC(SDP.LUyear))
   'SDP.LUmonth = month, binary
   'SDP.LUday = day, binary
   'SDP.HereSeg is set to this handle's control segment (location in memory)
ELSE
   'error
ENDIF

See: ReadDHXBsrc

~ReadDHXBsrc

Func: ReadDHXB         Pack: HandlePack         Func:  14/Mid-level

DIM HP AS HandlePack

HP.Func = ReadDHXB              'defined in BULLET.BI
HP.Handle = datahandle          'handle of file whose header you want to reload
stat = BULLET(HP)
IF stat THEN 'error

This routine is automatically called by the network lock routines.












See: FlushDHXBsrc  LockDataXB

~FlushDHXBsrc

Func: FlushDHXB        Pack: HandlePack         Func:  15/Mid-level

DIM HP AS HandlePack

HP.Func = FlushDHXB             'defined in BULLET.BI
HP.Handle = datahandle          'handle of file you want to flush
stat = BULLET(HP)
IF stat THEN 'error

Note that the physical write to disk is performed only if the file has changed
since the open or last flush.

This routine is automatically called by the network unlock routines.









See: CopyDHXBsrc  UnlockDataXB

~CopyDHXBsrc

Func: CopyDHXBsrc      Pack: CopyPack           Func:  16/Mid-level

DIM CP AS CopyPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()

TempStr = filename$ + CHR$(0)   'assign filename to a fixed-len string...

CP.Func = CopyDHXB              'defined in BULLET.BI
CP.Handle = datahandle          'handle of file to copy from (the source)
CP.FilenamePtrOff = VARPTR(TempStr) 'far pointer to filenameZ for copy
CP.FilenamePtrSeg = VARPTR(TempStr) '(the destination)
stat = BULLET(CP)
IF stat THEN 'error









See: ZapDHXBsrc

~ZapDHXBsrc

Func: ZapDHXB          Pack: HandlePack         Func:  17/Mid-level

DIM HP AS HandlePack

HP.Func = ZapDHXB               'defined in BULLET.BI
HP.Handle = datahandle          'handle of file you want !ZAP!
stat = BULLET(HP)
IF stat THEN 'error

Note that this removes ALL data records from the data file.












See: CreateKXBsrc

~CreateKXBsrc

Func: CreateKXB        Pack: CreateKeyPack      Func:  20/Mid-level

'This code assumes that the datafile was created as in CreateDXBsrc, and that
'the datafile was opened as in OpenDXBsrc.

kx$ = "SUBSTR(STUDENT,1,5)"     'key expression, a non-unique, character key

DIM CKP AS CreateKeyPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()
DIM TempStr2 AS STRING * 136    'used as fixed-length string for VARPTR()

TempStr = filename$ + CHR$(0)   'assign filename to a fixed-len string
TempStr2 = kx$ + CHR$(0)        'same for key expression string

'(cont)







See: CreateKXBsrc_a                                                     -MORE-

~CreateKXBsrc_a

CKP.Func = CreateKXB                 'defined in BULLET.BI
CKP.FilenamePtrOff = VARPTR(TempStr) 'far pointer to filenameZ
CKP.FilenamePtrSeg = VARSEG(TempStr)
CKP.KeyExpPtrOff = VARPTR(TempStr2)  'far pointer to key expressionZ
CKP.KeyExpPtrSeg = VARSEG(TempStr2)
CKP.XBlink = datahandle              'the datafile handle returned from OpenDXB
CKP.KeyFlags = cCHAR                 '-KEYFLAGS- are defined in BULLET.BI
CKP.CodePageID = -1                  'use DOS default code page ID
CKP.CountryCode = -1                 'use DOS default country code
CKP.CollatePtrOff = 0                'no user-supplied collate table...
CKP.CollatePtrSeg = 0                '...
stat = BULLET(CKP)
IF stat THEN 'error

Normally this code would be written as a generalized FUNCTION. The CKP could be
a global allocation (DIM SHARED CKP AS CreateKeyPack). A possible header:

 DECLARE FUNCTION CreateNewIndex% (filename$, datahandle, kx$, KeyFlags%)

To create an index file you'd need just filename, datahandle, key expression,
and key flags (country code info if not using default), then call the function.

See: OpenKXBsrc  CreateKXBsrc

~OpenKXBsrc

Func: OpenKXB          Pack: OpenPack           Func:  21/Mid-level

DIM OP AS OpenPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()

TempStr = filename$ + CHR$(0)   'assign filename to a fixed-len string...

OP.Func = OpenKXB                   'defined in BULLET.BI
OP.FilenamePtrOff = VARPTR(TempStr) 'point to filenameZ (Z=0-terminated str)
OP.FilenamePtrSeg = VARSEG(TempStr)
OP.ASmode = ReadWrite + DenyNone    'defined in BULLET.BI
OP.XBlink = datafilehandle          'OpenKXB needs to know the data file handle
stat = BULLET(OP)
IF stat THEN 'error

The ASmode (access/sharing mode) determines how the operating system controls
access to the file. See OpenFileDOS for the meanings of the various ASmodes.

Before you can open an index file you must first open its associated data file.



See: CloseKXBsrc  OpenFileDOS

~CloseKXBsrc

Func: CloseKXB         Pack: HandlePack         Func:  22/Mid-level

DIM HP AS HandlePack

HP.Func = CloseDXB              'defined in BULLET.BI
HP.Handle = indexhandle         'handle of the file to close
stat = BULLET(HP)
IF stat THEN 'error














See: StatKXBsrc

~StatKXBsrc

Func: StatKXB          Pack: StatKeyPack        Func:  23/Mid-level

DIM SKP AS StatKeyPack

SKP.Func = StatKXB          'defined in BULLET.BI
SKP.Handle = indexhandle    'handle to get stats on
stat = BULLET(SKP)          'must be index handle, use StatHandleXB if you...
IF stat = 0 THEN            '...don't know the type of file a handle's for
   'SKP.FileType is set to 0
   'SKP.Dirty is set to 1 if the file has changed (0=not changed)
   'SKP.Keys = number of key in the index file (index file=key file)
   'SKP.KeyLen = physical key length (1-64 bytes)
   'SKP.XBlink = datafile handle that this index file is associated with
   'SKP.XBrecno is set to record number associated with last accessed key
   'SKP.HereSeg is set to this handle's control segment (location in memory)
   'SKP.CodePageID returns this index file's permanent code page ID
   'SKP.CountryCode returns this index file's permanent country code
   'SKP.CollateTableSize = 0 (no collate table present) or 256 (table present)
   'SKP.KeyFlags = key flags specifed at CreateKXB (except NLS flag may be set)
ELSE                                               (NLS flag is bit 14, &H4000)
   'error

See: ReadKHXBsrc

~ReadKHXBsrc

Func: ReadKHXB         Pack: HandlePack         Func:  24/Mid-level

DIM HP AS HandlePack

HP.Func = ReadKHXB              'defined in BULLET.BI
HP.Handle = indexhandle         'handle of file whose header you want to reload
stat = BULLET(HP)
IF stat THEN 'error

This routine is automatically called by the network lock routines.












See: FlushKHXBsrc  LockKeyXB

~FlushKHXBsrc

Func: FlushKHXB        Pack: HandlePack         Func:  25/Mid-level

DIM HP AS HandlePack

HP.Func = FlushKHXB             'defined in BULLET.BI
HP.Handle = indexhandle         'handle of file you want to flush
stat = BULLET(HP)
IF stat THEN 'error

Note that the physical write to disk is performed only if the file has changed
since the open or last flush.

This routine is automatically called by the network unlock routines.









See: CopyKHXBsrc  UnlockKeyXB

~CopyKHXBsrc

Func: CopyKHXBsrc      Pack: CopyPack           Func:  26/Mid-level

DIM CP AS CopyPack
DIM TempStr AS STRING * 80      'used as fixed-length string for VARPTR()

TempStr = filename$ + CHR$(0)   'assign filename to a fixed-len string...

CP.Func = CopyKHXB              'defined in BULLET.BI
CP.Handle = indexhandle         'handle of file to copy from (the source)
CP.FilenamePtrOff = VARPTR(TempStr) 'far pointer to filenameZ for copy
CP.FilenamePtrSeg = VARPTR(TempStr) '(the destination)
stat = BULLET(CP)
IF stat THEN 'error









See: ZapKHXBsrc

~ZapKHXBsrc

Func: ZapKHXB          Pack: HandlePack         Func:  27/Mid-level

DIM HP AS HandlePack

HP.Func = ZapKHXB               'defined in BULLET.BI
HP.Handle = indexhandle         'handle of file you want !ZAP!
stat = BULLET(HP)
IF stat THEN 'error

Note that this removes ALL keys from the index file.












See: GetDescriptorXBsrc

~GetDescriptorXBsrc

Func: GetDescriptorXB  Pack: DescriptorPack     Func:  30/Mid-level

DIM DP AS DescriptorPack

DP.Func = GetDescriptorXB               'defined in BULLET.BI
DP.Handle = datahandle                  'handle of file
IF FieldNumber > 0 THEN
   DP.FieldNumber = FieldNumber         'field number to get info on
ELSE                                    'or, if 0, then
   DP.FieldNumber = 0
   DP.FD.FieldName = fieldname$ + STRING$(10,0)   'fieldname$ to get info on
ENDIF
stat = BULLET(DP)
IF stat = 0 THEN
   'DP.FD.FieldName is set to field name
   'DP.FD.FieldType is set to field type
   'DP.FD.FieldLen is set to field length
   'DP.FD.FieldDC is set to field DC
ELSE         ┌───────────────────────────────────────────────────────────────┐
   'error    │The DP.FD.FieldLen (and DP.FD.FieldDC) is a byte string so you │
             │need to use a FldLen=ASC(DP.FD.FieldLen) for its integer value │
             └───────────────────────────────────────────────────────────────┘
See: GetRecordXBsrc

~GetRecordXBsrc

Func: GetRecordXB      Pack: AccessPack         Func:  31/Mid-level

TYPE RecordTYPE                 'simple DBF record layout
tag AS STRING * 1  <───┌─────────────────────────────────────────────────┐
code AS STRING * 4     │THE FIRST BYTE OF YOUR RECORD TYPES MUST BE TAG! │
bday AS STRING * 8     └─────────────────────────────────────────────────┘
END TYPE
DIM RecBuff AS RecordTYPE       'record has 2 fields, code/C/4.0, bday/D/8.0
DIM AP AS AccessPack

AP.Func = GetRecordXB           'defined in BULLET.BI
AP.Handle = datahandle          'handle to get record from
AP.RecNo = RecnoToGet&          'record number to get
AP.RecPtrOff = VARPTR(RecBuff)  'read record from disk into RecBuff
AP.RecPtrSeg = VARSEG(RecBuff)
stat = BULLET(AP)
IF stat = 0 THEN
   PRINT RecBuff.code ; ", " ; RecBuff.bday     'sample output: 4321, 19331122
ELSE
   'error


See: AddRecordXBsrc

~AddRecordXBsrc

Func: AddRecordXB      Pack: AccessPack         Func:  32/Mid-level

TYPE RecordTYPE                 'simple DBF record layout
tag AS STRING * 1  <───┌─────────────────────────────────────────────────┐
code AS STRING * 4     │THE FIRST BYTE OF YOUR RECORD TYPES MUST BE TAG! │
bday AS STRING * 8     └─────────────────────────────────────────────────┘
END TYPE
DIM RecBuff AS RecordTYPE       'record has 2 fields, code/C/4.0, bday/D/8.0
DIM AP AS AccessPack

'be sure to init the tag field to a space (ASCII 32)
RecBuff.tag = " " : RecBuff.code = "1234" : RecBuff.bday = "19331122"

AP.Func = AddRecordXB           'defined in BULLET.BI
AP.Handle = datahandle          'handle to add record to
AP.RecPtrOff = VARPTR(RecBuff)  'write record from RecBuff to disk...
AP.RecPtrSeg = VARSEG(RecBuff)  '...at next available record number
stat = BULLET(AP)
IF stat = 0 THEN
   PRINT "Record number used by AddRecordXB was "; AP.RecNo
ELSE 'error

See: UpdateRecordXBsrc

~UpdateRecordXBsrc

Func: UpdateRecordXB   Pack: AccessPack         Func:  33/Mid-level

'see GetRecordXBsrc for this source example's preliminary code

AP.Func = GetRecordXB           'first get the record to update
AP.Handle = datahandle
AP.RecNo = RecnoToGet&          '┌───────────────────────────────────────┐
AP.RecPtrOff = VARPTR(RecBuff)  '│ Do NOT use UpdateRecordXB to change   │
AP.RecPtrSeg = VARSEG(RecBuff)  '│ any field(s) used in a key expression.│
stat = BULLET(AP)               '│ Instead use UpdateXB.                 │
IF stat = 0 THEN                '└───────────────────────────────────────┘
   RecBuff.dbay = "19591122"    'change only non-key portions of record
   AP.Func = UpdateRecordXB     'defined in BULLET.BI
   stat = BULLET(AP)            'other AP. values are already set from the Get
   IF stat THEN                 'though you should reassign AP.RecPtrOff/Seg if
      'error                    'you perform any BASIC string functions between
┌──────────────────────────────┐'BULLET calls (none were in this case).
│NOTE: QB/PDS will move strings│'--by this I mean to set these again
│after a user-break/Ctrl-Break │'  AP.RecPtrOff = VARPTR(RecBuff)
│in the QB/QBX environment !!! │'  AP.RecPtrSeg = VARSEG(RecBuff)
└──────────────────────────────┘'  stat = BULLET(AP) 'do the update call

See: DeleteRecordXBsrc  UpdateXB

~DeleteRecordXBsrc

Func: DeleteRecordXB   Pack: AccessPack         Func:  34/Mid-level

DIM AP AS AccessPack

AP.Func = DeleteRecordXB        'defined in BULLET.BI
AP.Handle = datahandle          'handle of record to delete
AP.RecNo = RecnoToDelete&       'to determine which record number any record
stat = BULLET(AP)               'is, use one of the keyed access routines
IF stat THEN 'error













See: UndeleteRecordsrc (XB)

~UndeleteRecordsrc (XB)

Func: UndeleteRecordXB Pack: AccessPack         Func:  35/Mid-level

DIM AP AS AccessPack

AP.Func = UndeleteRecordXB      'defined in BULLET.BI
AP.Handle = datahandle          'handle of record to undelete
AP.RecNo = RecnoToUndelete&     'to determine which record number any record
stat = BULLET(AP)               'is use one of the keyed access routines
IF stat THEN 'error













See: PackRecordsXBsrc

~PackRecordsXBsrc

Func: PackRecordsXB    Pack: AccessPack         Func:  36/Mid-level

DIM AP AS AccessPack

AP.Func = PackRecordsXB         'defined in BULLET.BI
AP.Handle = datahandle          'handle of data file to pack
stat = BULLET(AP)
IF stat THEN 'error














See: FirstKeyXBsrc

~FirstKeyXBsrc

Func: FirstKeyXB       Pack: AccessPack         Func:  40/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
                                'needs to be at least key length (MAXKEYLEN=64)
AP.Func = FirstKeyXB            'defined in BULLET.BI
AP.Handle = indexhandle         'handle to index file to access key from
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the first key is read from disk and put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'TempStr is filled in with the key (for as many bytes as the key length)
   'so, if the key is a character type, you could PRINT LEFT$(TempStr,KeyLen)
   'or, if key is a 32-bit LONG then PRINT CVL(LEFT$(TempStr,4)). When using
   'the key returned, be aware that if UNIQUE was NOT specified then the
   'enumerator word is attached to the end of the key (the right two bytes).
   'Also, AP.RecNo is set to the record number of the first key in the index.
ELSE
   'error



See: EqualKeyXBsrc

~EqualKeyXBsrc

Func: EqualKeyXB       Pack: AccessPack         Func:  41/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
                                'passed key to find, start with enum=0
TempStr = FindKey$ + CHR$(0) + CHR$(0) + CHR$(0) '2 enumerator bytes + 0-term

AP.Func = EqualKeyXB            'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file to find key from
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)
stat = BULLET(AP)
IF stat = 0 THEN
   'the key matched exactly (including enumerator, if present)
   'TempStr is NOT ALTERED
   'AP.RecNo is set to the record number of that key
ELSEIF stat = 200 THEN
   AP.Func = NextKeyXB          'if not found, get following key and check the
   stat = BULLET(AP)            'key proper (key less the enumerator bytes)
   IF stat = 0 THEN             '(i.e., it may be proper key but enumerator=1)
      'see NextKeyXBsrc for continuation

See: NextKeyXBsrc

~NextKeyXBsrc

Func: NextKeyXB        Pack: AccessPack         Func:  42/Mid-level

'see EqualKeyXBsrc for preliminary code

AP.Func = NextKeyXB             'defined in BULLET.BI
stat = BULLET(AP)               'KEYLEN assumed to equal actual key length...
IF stat = 0 THEN                '...as returned by StatKXB
    IF IndexFileIsNotUnique THEN
       IF LEFT$(TempStr,KeyLen-2) = FindKey$ THEN 'the next key matches!
                                                  '(except for the enumerator)

This code example follows up on the EqualKeyXBsrc example. See EqualKeyXB for
more information on finding partial keys.









See: PrevKeyXBsrc  EqualKeyXBsrc

~PrevKeyXBsrc

Func: PrevKeyXB        Pack: AccessPack         Func:  43/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()

'assume code has already executed to locate a key--this code then gets the key
'before that one

AP.Func = PrevKeyXB             'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file to access key from
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the prev key is read from disk and put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'TempStr is filled in with the key (for as many bytes as the key length).
   'Also, AP.RecNo is set to the record number of the key.
ELSE
   'error




See: LastKeyXBsrc

~LastKeyXBsrc

Func: LastKeyXB        Pack: AccessPack         Func:  44/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()

AP.Func = LastKeyXB             'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file to access key from
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the last key is read from disk and put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'TempStr is filled in with the key (for as many bytes as the key length)
   'AP.RecNo is set to the record number of the last key.
ELSE
   'error







See: StoreKeyXBsrc

~StoreKeyXBsrc

Func: StoreKeyXB       Pack: AccessPack         Func:  45/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()

'Assume record has been added to data file (AddRecordXB, returning RecNo2Use)
'and key has been built (BuildKeyXB returning KeyToAdd$).
              
TempStr = KeyToAdd$ + CHR$(0)   'add the key to a UNIQUE index file

AP.Func = StoreKeyXB            'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file to insert key into
AP.RecNo = RecNo2Use            'associate this record number with key
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the last key is read from disk and put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'key added
ELSEIF stat = 201 THEN
   'key already exists, which means you need to construct a unique enumerator--
   'provided the file wasn't created for UNIQUE keys...INSTEAD USE InsertXB!

See: DeleteKeyXBsrc  InsertXB

~DeleteKeyXBsrc

Func: DeleteKeyXB      Pack: AccessPack         Func:  46/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()

TempStr = KeyToDelete$ + CHR$(0)   'delete the key from a UNIQUE index file
                                   '(else need to supply enumerator also)
AP.Func = DeleteKeyXB           'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file of key to delete
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(this key is searched for exactly)
stat = BULLET(AP)               'if exact match found the key is deleted!
IF stat = 0 THEN
   'key deleted permanently
ELSEIF stat = 200 THEN
   'key as stated was not in the index file--if the index is not UNQIUE then
   'you must supply the exact enumerator along with the key proper to delete
   '--you can use the CurrentKeyXB routine to obtain the exact current key
ELSE
   'other error


See: BuildKeyXBsrc

~BuildKeyXBsrc

Func: BuildKeyXB       Pack: AccessPack         Func:  47/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()

'Assume record has been built and is ready to be added to the data file. The
'record is in the variable RecBuff (a fixed-length TYPE variable, typically).

AP.Func = BuildKeyXB            'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file key is to be built for
AP.RecPtrOff = VARPTR(RecBuff)  'far pointer to data record buffer
AP.RecPtrSeg = VARSEG(RecBuff)
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the built key is put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'key built okay so can do a AddRecordXB followed by a StoreKeyXB
   'but, again, InsertXB takes care of all this detail and then some--use it
ELSE
   'error


See: CurrentKeyXBsrc

~CurrentKeyXBsrc

Func: CurrentKeyXB     Pack: AccessPack         Func:  48/Mid-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()

AP.Func = CurrentKeyXB          'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the current key is put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'TempStr set to current key (valid only for KeyLen bytes)
   'Also, AP.RecNo is set to the record number of the key.
ELSE
   'error







See: GetFirstXBsrc

~GetFirstXBsrc

Func: GetFirstXB       Pack: AccessPack         Func:  60/High-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
DIM RecBuff AS RecordTYPE       'see AddRecordXBsrc for record layout

AP.Func = GetFirstXB            'defined in BULLET.BI
AP.Handle = indexhandle         'handle to index file to access key from
AP.RecPtrOff = VARPTR(RecBuff)  'far pointer to record buffer
AP.RecPtrSeg = VARSEG(RecBuff)  '(the record indexed by the key is put here)
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the first key is read from disk and put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'TempStr is filled in with the key (for as many bytes as the key length)
   'RecBuff is filled in with the data record
   'AP.RecNo is set to the record number of the first key in the index.
ELSE
   'error



See: GetEqualXBsrc

~GetEqualXBsrc

Func: GetEqualXB       Pack: AccessPack         Func:  61/High-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
DIM RecBuff AS RecordTYPE       'see AddRecordXBsrc for record layout
                                'passed key to find, start with enum=0
TempStr = FindKey$ + CHR$(0) + CHR$(0) + CHR$(0) '2 enumerator bytes + 0-term

AP.Func = GetEqualXB            'defined in BULLET.BI
AP.Handle = indexfile           'handle to index file to find key from
AP.RecPtrOff = VARPTR(RecBuff)  'far pointer to record buffer
AP.RecPtrSeg = VARSEG(RecBuff)  '(the record indexed by the key is put here)
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)
stat = BULLET(AP)
IF stat = 0 THEN
   'RecBuff, and AP.RecNo filled as expected (TempStr remains the same)
ELSEIF stat = 200 THEN
   AP.Func = GetNextXB          'if not found, can get following key--the next
   stat = BULLET(AP)            'key would logically follow the key not found
                                '--this let's you search based on partial keys

See: GetNextXBsrc

~GetNextXBsrc

Func: GetNextXB        Pack: AccessPack         Func:  62/High-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
DIM RecBuff AS RecordTYPE       'see AddRecordXBsrc for record layout

AP.Func = GetFirstXB            'defined in BULLET.BI
AP.Handle = indexhandle         'handle to index file to access key from
AP.RecPtrOff = VARPTR(RecBuff)  'far pointer to record buffer
AP.RecPtrSeg = VARSEG(RecBuff)  '(the record indexed by the key is put here)
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the first key is read from disk and put here)
stat = BULLET(AP)
DO WHILE stat = 0               'print all records in key order
   PRINT AP.RecNo; RecBuff.code; ", "; RecBuff.bday
   AP.Func = GetNextXB
   stat = BULLET(AP)
LOOP
IF stat <> 202 THEN             'error 202 means end of file (expected)
   'error other than expected EOF


See: GetPrevXBsrc

~GetPrevXBsrc

Func: GetPrevXB        Pack: AccessPack         Func:  63/High-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
DIM RecBuff AS RecordTYPE       'see AddRecordXBsrc for record layout

AP.Func = GetLastXB             'defined in BULLET.BI
AP.Handle = indexhandle         'handle to index file to access key from
AP.RecPtrOff = VARPTR(RecBuff)  'far pointer to record buffer
AP.RecPtrSeg = VARSEG(RecBuff)  '(the record indexed by the key is put here)
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the last key is read from disk and put here)
stat = BULLET(AP)
DO WHILE stat = 0               'print all records in REVERSE key order
   PRINT AP.RecNo; RecBuff.code; ", "; RecBuff.bday
   AP.Func = GetPrevXB
   stat = BULLET(AP)
LOOP
IF stat <> 203 THEN             'error 203 means top of file (expected)
   'error other than expected TOF


See: GetLastXBsrc

~GetLastXBsrc

Func: GetLastXB        Pack: AccessPack         Func:  64/High-level

DIM AP AS AccessPack
DIM TempStr AS STRING * 64      'used as fixed-length string for VARPTR()
DIM RecBuff AS RecordTYPE       'see AddRecordXBsrc for record layout

AP.Func = GetLastXB             'defined in BULLET.BI
AP.Handle = indexhandle         'handle to index file to access key from
AP.RecPtrOff = VARPTR(RecBuff)  'far pointer to record buffer
AP.RecPtrSeg = VARSEG(RecBuff)  '(the record indexed by the key is put here)
AP.KeyPtrOff = VARPTR(TempStr)  'far pointer to key buffer
AP.KeyPtrSeg = VARSEG(TempStr)  '(the last key is read from disk and put here)
stat = BULLET(AP)
IF stat = 0 THEN
   'TempStr is filled in with the key (for as many bytes as the key length)
   'RecBuff is filled in with the data record
   'AP.RecNo is set to the record number of the last key in the index.
ELSE
   'error



See: InsertXBsrc

~InsertXBsrc

Func: InsertXB         Pack: AccessPack         Func:  65/High-level

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'TempStr and RecBuff previously defined
FOR i = 1 TO 3                  '3=number of related indexes to maintain
   AP(i).Func = InsertXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).RecPtrOff = VARPTR(RecBuff) ┌─────────────────────────────────────┐
   AP(i).RecPtrSeg = VARSEG(RecBuff) │ see AddRecordXBsrc for RecBuff TYPE │
   AP(i).KeyPtrOff = VARPTR(TempStr) │(be sure you reserved the tag field) │
   AP(i).KeyPtrSeg = VARSEG(TempStr) └─────────────────────────────────────┘
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))   'AP(1 TO 4) to avoid subscript error
NEXT
AP(3).NextPtrOff = 0            'reset last access pack to end-link value
AP(3).NextPtrSeg = 0
stat = BULLET(AP(1))
IF stat = 0 THEN                'if stat=0 must still check AP(1).Stat
   IF AP(1).Stat <> 0 THEN      'error when adding data record
ELSE
   TrueError = AP(stat).Stat    'the returned stat is array index of bad pack

See: UpdateXBsrc

~UpdateXBsrc

Func: UpdateXB         Pack: AccessPack         Func:  66/High-level

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'TempStr and RecBuff previously defined
FOR i = 1 TO 3                  '3=number of related indexes to maintain
   AP(i).Func = UpdateXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).RecNo = RecordNumberToUpdate&    'tell it which record to update
   AP(i).RecPtrOff = VARPTR(RecBuff)      'RecBuff has new, updated data
   AP(i).RecPtrSeg = VARSEG(RecBuff)
   AP(i).KeyPtrOff = VARPTR(TempStr)
   AP(i).KeyPtrSeg = VARSEG(TempStr)
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))   'AP(1 TO 4) to avoid subscript error
NEXT
AP(3).NextPtrOff = 0 : AP(3).NextPtrSeg = 0 'reset last access pack to 0
stat = BULLET(AP(1))
IF stat = 0 THEN                'if stat=0 must still check AP(1).Stat
   IF AP(1).Stat <> 0 THEN      'error when writing data record
ELSE
   TrueError = AP(stat).Stat    'the returned stat is array index of bad pack

See: ReindexXBsrc

~ReindexXBsrc

Func: ReindexXB        Pack: AccessPack         Func:  67/High-level

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'DIM'd to 4 to avoid bad subscript in loop
FOR i = 1 TO 3                  '3=number of related indexes to reindex
   AP(i).Func = ReindexXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))   'AP(1 TO 4) to avoid subscript error
NEXT
AP(3).NextPtrOff = 0
AP(3).NextPtrSeg = 0            'reset last access pack to end-link value
stat = BULLET(AP(1))
IF stat THEN                    'if stat <> 0 then the...
   TrueError = AP(stat).Stat    '...returned stat is array index of bad pack

┌─────────────────────────────────────────────────────────────────────────────┐
│The reason AP() is REDIM AP(1 TO 4) is so that the AP(i + 1) in the code loop│
│doesn't create an invalid subscript error.                                   │
└─────────────────────────────────────────────────────────────────────────────┘


See: LockXBsrc

~LockXBsrc

Func: LockXB           Pack: AccessPack         Func:  80/Network

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'DIM'd to 4 to avoid bad subscript in loop
FOR i = 1 TO 3                  '3=number of related indexes to Lock
   AP(i).Func = LockXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))
NEXT                            'data file handle known internally by BULLET
AP(3).NextPtrOff = 0
AP(3).NextPtrSeg = 0            'reset last access pack to end-link value
stat = BULLET(AP(1))
IF stat > 3 THEN                'if stat > 3 (> number of packs) then the...
   TrueError = AP(3).Stat       '...lock failed on the data file
ELSEIF stat <> 0 THEN                                          (|last ReadKHXB)
   TrueError = AP(stat).Stat    '...lock failed on index file # stat

┌─────────────────────────────────────────────────────────────────────────────┐
│The Lock routines use a different method to identify the bad pack when the   │
│failure was caused by the data file. See above.                              │
└─────────────────────────────────────────────────────────────────────────────┘
See: UnlockXBsrc

~UnlockXBsrc

Func: UnlockXB         Pack: AccessPack         Func:  81/Network

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'DIM'd to 4 to avoid bad subscript in loop
FOR i = 1 TO 3                  '3=number of related indexes to Lock
   AP(i).Func = UnlockXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))
NEXT                            'data file handle known internally by BULLET
AP(3).NextPtrOff = 0
AP(3).NextPtrSeg = 0            'reset last access pack to end-link value
stat = BULLET(AP(1))
IF stat > 3 THEN                'if stat > 3 (> number of packs) then the...
   TrueError = AP(3).Stat       '...unlock failed on the data file
ELSEIF stat <> 0 THEN
   TrueError = AP(stat).Stat    '...unlock failed on index file # stat

┌─────────────────────────────────────────────────────────────────────────────┐
│The Lock routines use a different method to identify the bad pack when the   │
│failure was caused by the data file. See above.                              │
└─────────────────────────────────────────────────────────────────────────────┘
See: LockKeyXBsrc

~LockKeyXBsrc

Func: LockKeyXB        Pack: AccessPack         Func:  82/Network

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'DIM'd to 4 to avoid bad subscript in loop
FOR i = 1 TO 3                  '3=number of related indexes to Lock
   AP(i).Func = LockXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))
NEXT
AP(3).NextPtrOff = 0
AP(3).NextPtrSeg = 0            'reset last access pack to end-link value
stat = BULLET(AP(1))
IF stat <> 0 THEN
   TrueError = AP(stat).Stat    'lock failed on index file # stat
                                '--if stat > 3 then failed on last internal
                                '--ReadKHXB...This is EXTREMELY unlikely





See: UnlockKeyXBsrc

~UnlockKeyXBsrc

Func: UnlockKeyXB      Pack: AccessPack         Func:  83/Network

REDIM AP(1 TO 4) AS AccessPack  'array of 3 access packs, 1 for each index file
                                'DIM'd to 4 to avoid bad subscript in loop
FOR i = 1 TO 3                  '3=number of related indexes to Lock
   AP(i).Func = UnlockKeyXB
   AP(i).Handle = indexhandle(i)          'each index file's handle
   AP(i).NextPtrOff = VARPTR(AP(i + 1))   'point to NEXT access pack
   AP(i).NextPtrSeg = VARSEG(AP(i + 1))
NEXT
AP(3).NextPtrOff = 0
AP(3).NextPtrSeg = 0            'reset last access pack to end-link value
stat = BULLET(AP(1))
IF stat <> 0 THEN
   TrueError = AP(stat).Stat    'unlock failed on index file # stat







See: LockDataXBsrc

~LockDataXBsrc

Func: LockDataXB       Pack: AccessPack         Func:  84/Network

DIM AP AS AccessPack

AP.Func = LockDataXB            'defined in BULLET.BI
AP.Handle = datahandle          'handle of data file to lock
AP.RecNo = 0&                   '=0 to lock all or, set to actual record number
stat = BULLET(AP)               '   to lock as in AP.RecNo = lockThisRec&
IF stat THEN 'error













See: UnlockDataXBsrc

~UnlockDataXBsrc

Func: UnlockDataXB     Pack: AccessPack         Func:  85/Network

DIM AP AS AccessPack

AP.Func = UnlockDataXB          'defined in BULLET.BI
AP.Handle = datahandle          'handle of data file to unlock
AP.RecNo = 0&                   '=0 to unlock all or, set to actual record num
stat = BULLET(AP)               '   to unlock as in AP.RecNo = lockThisRec&
IF stat THEN 'error

                                'note: you cannot unlock parts of a file with
                                '1 single unlock (where AP.RecNo=0). Instead,
                                'you must unlock each record individually--
                                'that is, if you made any single-record locks








See: DriveRemoteXBsrc

~DriveRemoteXBsrc

Func: DriveRemoteXB    Pack: RemotePack         Func:  86/Network

DIM RP AS RemotePack

RP.Func = DriveRemoteXB         'defined in BULLET.BI
RP.Handle = drive2check         'drive to check (0=default, 1=A:,2=B:,3=C:...)
stat = BULLET(RP)
IF stat = 0 THEN
   'RP.IsRemote set to 0 if drive local, 1 if remote
   'RP.Flags set to DX register as returned by DOS
   'RP.IsShare set to 0 if SHARE.EXE is not loaded, non-zero SHARE installed
ELSE
  'error  (like invalid drive)









See: FileRemoteXBsrc

~FileRemoteXBsrc

Func: FileRemoteXB     Pack: RemotePack         Func:  87/Network

DIM RP AS RemotePack

RP.Func = FileRemoteXB          'defined in BULLET.BI
RP.Handle = filehandle          'file handle to check
stat = BULLET(RP)
IF stat = 0 THEN
   'RP.IsRemote set to 0 if file local, 1 if remote
   'RP.Flags set to DX register as returned by DOS
   'RP.IsShare set to 0 if SHARE.EXE is not loaded, non-zero SHARE installed
ELSE
  'error  (like invalid handle)









See: SetRetriesXBsrc

~SetRetriesXBsrc

Func: SetRetriesXB     Pack: SetRetriesPack     Func:  88/Network

DIM SRP AS SetRetriesPack

SRP.Func = SetRetriesXB
SRP.Mode = 1             '1=set to user values, 0=set DOS default
SRP.Pause = 5000         'do 5,000 loops between retries
SRP.Retries = 5          'try 5 times before giving up with error
stat = BULLET(SRP)
IF stat THEN
   'error                'it's unlikely an error occurs











See: DeleteFileDOSsrc

~DeleteFileDOSsrc

Func: DeleteFileDOS    Pack: DOSFilePack        Func: 100/DOS

DIM DFP AS DOSFilePack
DIM TempStr AS STRING * 80

TempStr = file2delete$ + CHR$(0)

DFP.Func = DeleteFileDOS        'defined in BULLET.BI
DFP.FilenamePtrOff = VARPTR(TempStr)
DFP.FilenamePtrSeg = VARSEG(TempStr)
stat = BULLET(DFP)
IF stat THEN 'error










See: RenameFileDOSsrc

~RenameFileDOSsrc

Func: RenameFileDOS    Pack: DOSFilePack        Func: 101/DOS

DIM DFP AS DOSFilePack
DIM TempStr AS STRING * 80
DIM TempStr2 AS STRING * 80

TempStr = file2rename$ + CHR$(0)
TempStr2 = newfilename$ + CHR$(0)

DFP.Func = RenameFileDOS        'defined in BULLET.BI
DFP.FilenamePtrOff = VARPTR(TempStr)
DFP.FilenamePtrSeg = VARSEG(TempStr)
DFP.NewNamePtrOff = VARPTR(TempStr2)
DFP.NewNamePtrSeg = VARSEG(TempStr2)
stat = BULLET(DFP)
IF stat THEN 'error






See: CreateFileDOSsrc

~CreateFileDOSsrc

Func: CreateFileDOS    Pack: DOSFilePack        Func: 102/DOS

DIM DFP AS DOSFilePack
DIM TempStr AS STRING * 80

TempStr = file2create$ + CHR$(0)

DFP.Func = CreateFileDOS        'defined in BULLET.BI
DFP.FilenamePtrOff = VARPTR(TempStr)
DFP.FilenamePtrSeg = VARSEG(TempStr)
DFP.Attr = 0                    'normal file directory attribute
stat = BULLET(DFP)
IF stat THEN 'error









See: AccessFileDOSsrc

~AccessFileDOSsrc

Func: AccessFileDOS    Pack: DOSFilePack        Func: 103/DOS

DIM DFP AS DOSFilePack
DIM TempStr AS STRING * 80

TempStr = file2access$ + CHR$(0)

DFP.Func = AccessFileDOS        'defined in BULLET.BI
DFP.FilenamePtrOff = VARPTR(TempStr)
DFP.FilenamePtrSeg = VARSEG(TempStr)
DFP.ASmode = &H42               'attempt R/W DENY NONE access
stat = BULLET(DFP)
IF stat THEN 'error









See: OpenFileDOSsrc

~OpenFileDOSsrc

Func: OpenFileDOS      Pack: DOSFilePack        Func: 104/DOS

DIM DFP AS DOSFilePack
DIM TempStr AS STRING * 80

TempStr = file2open$ + CHR$(0)

DFP.Func = OpenFileDOS          'defined in BULLET.BI
DFP.FilenamePtrOff = VARPTR(TempStr)
DFP.FilenamePtrSeg = VARSEG(TempStr)
DFP.ASmode = &H42               'open in R/W DENY NONE access
stat = BULLET(DFP)
IF stat = 0 THEN
   'DFP.Handle set to handle of open file
ELSE
   'error






See: SeekFileDOSsrc

~SeekFileDOSsrc

Func: SeekFileDOS      Pack: DOSFilePack        Func: 105/DOS

DIM DFP AS DOSFilePack

DFP.Func = SeekFileDOS          'defined in BULLET.BI
DFP.Handle = handle
DFP.SeekOffset = 0&             'position 0 relative EOF (get length of file)
DFP.Method = 2                  'seek from END of file
stat = BULLET(DFP)
IF stat = 0 THEN
   'DFP.SeekOffset set to absolute current offset
   '--in this case, the DFP.SeekOffset equals then length of the file
ELSE
   'error








See: ReadFileDOSsrc

~ReadFileDOSsrc

Func: ReadFileDOS      Pack: DOSFilePack        Func: 106/DOS

DIM DFP AS DOSFilePack
DIM ReadBuff AS STRING * 512    'sector buffer

DFP.Func = ReadFileDOS          'defined in BULLET.BI
DFP.Handle = handle
DFP.Bytes = Bytes2Read          '16-bit value, in this case 512 since that's
DFP.BufferPtrOff = VARPTR(ReadBuff)            'the size of ReadBuff
DFP.BufferPtrSeg = VARSEG(ReadBuff)
stat = BULLET(DFP)
IF stat = 0 THEN
   IF DFP.Bytes <> Bytes2Read THEN              'check if EOF processed
      'hit EOF before reading all 512 bytes
   ELSE
      'ReadBuff filled with 512 bytes of data read from the current disk pos
      'disk position moved to the last byte read + 1
   ENDIF
ELSE
   'error


See: ExpandFileDOSsrc

~ExpandFileDOSsrc

Func: ExpandFileDOS    Pack: DOSFilePack        Func: 107/DOS

DIM DFP AS DOSFilePack

DFP.Func =  ExpandFileDOS       'defined in BULLET.BI
DFP.Handle = handle
DFP.SeekOffset = Bytes2ExpandBy&
stat = BULLET(DFP)
IF stat = 0 THEN
   'file expanded by number of bytes specified
ELSE
   'error










See: WriteFileDOSsrc

~WriteFileDOSsrc

Func: WriteFileDOS     Pack: DOSFilePack        Func: 108/DOS

DIM DFP AS DOSFilePack
DIM WriteBuff AS STRING * 512   'sector buffer

DFP.Func = WriteFileDOS         'defined in BULLET.BI
DFP.Handle = handle
DFP.Bytes = Bytes2Write         '16-bit value, in this case 512 since that's
DFP.BufferPtrOff = VARPTR(WriteBuff)           'the size of WriteBuff
DFP.BufferPtrSeg = VARSEG(WriteBuff)
stat = BULLET(DFP)
IF stat = -2 THEN
   'disk full
ELSE IF stat THEN
   'other error
ELSE
   'okay

Unlike ReadFileDOS, if the number of bytes actually written does not equal
Bytes2Write, the WriteFileDOS routine returns a DISK FULL error code (-2).


See: CloseFileDOSsrc

~CloseFileDOSsrc

Func: CloseFileDOS     Pack: DOSFilePack        Func: 109/DOS

DIM DFP AS DOSFilePack

DFP.Func = CloseFileDOS         'defined in BULLET.BI
DFP.Handle =handle2close
stat = BULLET(DFP)
IF stat THEN 'error














See: MakeDirDOSsrc

~MakeDirDOSsrc

Func: MakeDirDOS       Pack: DOSFilePack        Func: 110/DOS

DIM DFP AS DOSFilePack
DIM TempStr AS STRING * 80

TempStr = subdir2make$ + CHR$(0)

DFP.Func = MakeDirDOS           'defined in BULLET.BI
DFP.FilenamePtrOff = VARPTR(TempStr)
DFP.FilenamePtrSeg = VARSEG(TempStr)
stat = BULLET(DFP)
IF stat THEN 'error










See: DeleteFileDOSsrc

~END
