home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / btree / btriev14 / btp.doc next >
Encoding:
Text File  |  1991-10-27  |  21.5 KB  |  479 lines

  1. BTP.DOC
  2. (C) 1991 John C. Leon
  3. Documentation for BTP V1.4, 10/27/91
  4. BTP - The Btrieve Unit for Turbo Pascal 6.0
  5. (* ------------------------------------------------------------------------ *)
  6.  
  7.  
  8. INTRODUCTION TO BTP
  9. -------------------
  10.  
  11. Btrieve is a record manager sold by Novell.  Btrieve has been around a long
  12. time.  It takes many forms today...with versions for Dos, Windows 3.0,
  13. OS/2, and a "client/server" form when used with Novell Netware.  Btrieve
  14. files have no inherent structure to differentiate fields.  Your own code
  15. provides that functionality.  This unit has been tested only with Btrieve for
  16. Dos, Version 5.10a, with all patches available thru 10/27/91 applied.
  17.  
  18. The "universal" Btrieve call, which is made by calling a TP function supplied
  19. by Novell with the Btrieve product, takes six parameters.  Keeping track of
  20. multiple Btrieve files and the variables for all six parameters for each
  21. file takes discipline and attention to detail.  Btrieve programming screams
  22. out for the simplification possible with object-oriented programming and the
  23. record structures provided by Turbo Pascal 6.0.
  24.  
  25. The BTP product was born to remove the drudgery from Btrieve programming.  BTP
  26. includes a number of sound data structures, including four objects: TRecMgr,
  27. BFile, BFixed, and BFileExt.  TRecMgr and BFile are base objects; they are
  28. direct descendants of TP6's TObject.  BFixed and BFileExt are both direct
  29. descendants of BFile.
  30.  
  31. TRECMGR  is of limited usefulness (see the unit source code and VERSION.PAS),
  32. -------  but it does provide the ability to make non-file oriented calls
  33.          (stop, reset, version, et al).
  34.  
  35. BFILE    is what you'll use for virtually all standard, fixed length Btrieve
  36. -----    files when not using extended calls; it will probably be your
  37.          workhorse.
  38.  
  39. BFIXED   has structures to support ANY standard, fixed length Btrieve file.
  40. ------   BFixed is used in the CRUNCHx.PAS programs provided, which can 'clone
  41.          and squish' (remove dead space) from any standard, fixed length
  42.          Btrieve file.  BFixed is useful for working on standard fixed length
  43.          files of an unknown nature when you need to perform *record* oriented
  44.          functions with no care about field definitions.
  45.  
  46. BFILEEXT is used for any Btrieve file for which you'll be using extended calls
  47. -------- (note there is no specific support for the extended insert call, but
  48.          CRUNCH2.PAS and CRUNCH3.PAS contain examples of how to make such
  49.          calls).
  50.  
  51. Quite simply, your TYPE declarations define your Btrieve files as descendants
  52. of one of the three file-oriented objects provided in BTP and describe above.
  53. Those declarations can include definitions of your file's field structures,
  54. and provide the required data and key buffers.  These programmer-defined
  55. additions to the BTP objects are encapsulated in the descendant object, along
  56. with the data fields inherited, which include a description of the file's
  57. structure and the file's position block.  Immediately upon instantiation of a
  58. BTP descendant object, you have access to any file's structure and stats.  You
  59. also have an 'isolated' position block and the required buffers for Btrieve
  60. calls.
  61.  
  62. In the case of a BFile descendant, you override (replace) a single object
  63. method, and can make your Btrieve calls with just two parameters: the Btrieve
  64. op code and key number.  Use of the 'read' extended calls (the 2 get extended
  65. calls and the 2 step extended calls) is only marginally more difficult.  In
  66. order to use extended calls, your object must be a descendant of BFileExt, and
  67. must override two methods: one for standard calls, and one for the extended
  68. calls.  BFileExt includes pointers to two collections, which are automatically
  69. initialized when the object's constructor is called.  The first collection is
  70. for the filter logic terms, the second is for the field extractor specs.  Your
  71. program simply inserts items into these collections.  The rest, including
  72. determining buffer lengths and structuring the outgoing buffer, is handled
  73. internally by the object's methods.
  74.  
  75. Note that this unit's initialization section does a HALT if the Btrieve
  76. record manager isn't resident...you never have to code the test yourself.
  77. Just use this unit!
  78.  
  79.  
  80. USING THE BFILE OBJECT
  81. ----------------------
  82.  
  83. This unit gives you a technique, a shorthand way to get into Btrieve files
  84. and manipulate them.  For example, to open a file and encapsulate its stats
  85. into your Btrieve file object, you need only do:
  86.  
  87.      ObjectName^.Init(BDosFileName, Normal)
  88.      (BDosFileName is a valid Btrieve file name, and 'Normal' is the open
  89.       mode...see BTP.PAS for the various constants defined.)
  90.  
  91. Similary, to close a file, you simply do:
  92.  
  93.      BStatus := ObJectName^.Close;
  94.  
  95. The BFile object defined herein is the heart of this unit.  BFile is NOT an
  96. abstract object!!  It can be used as is, for example, if all you want to do
  97. is to get stats (see STATS.PAS).  However, if you wish to do any normal
  98. record-oriented functions, you will want to extend the object by adding one
  99. data element, a free-union variant record, to the BFile definition.  This
  100. record will contain your field definitions.  Lastly, you are *required* to
  101. override the BT function.  Here's an example.
  102. ..............................................................................
  103. TYPE
  104.  
  105.   MyFields  = record
  106.               case integer of
  107.               1: (Field1 : array[1..4] of char;  (obviously use size and type)
  108.                   Field2 : array[1..10] of char; (of your fields here!       )
  109.                   (rest of fields go here also)
  110.                   KeyBuf : array[1..4] of char); (size to largest key length )
  111.               2: (DBuffer: array[1..14] of char);(size to record length      )
  112.               3: (Position: array[1..2] of word);(high word returned first!  )
  113.                   end;                           (useful after a GET POSITION)
  114.  
  115.   PMyObject = ^MyObject;
  116.   MyObject  = object(BFile)        (MyObject is now a descendant of BFile.   )
  117.               Fields : MyFields;
  118.               function BT(OpCode, Key: integer); integer; virtual;
  119.               end;
  120.  
  121. VAR
  122.  
  123.   MyFile    : PMyObject;
  124.  
  125.  
  126. (This is the required override of the BT function for all standard, fixed
  127.  length files.  As the base object has no field structures, it cannot include
  128.  this function...you must override it by REPLACING it as shown here.)
  129.  
  130. function MyObject.BT(OpCode, Key:integer);integer;
  131. begin                             (DBufferLen is reset here as it may need to)
  132.    DBufferLen := Specs.RecLen;    (be changed on return from some ops.       )
  133.    BT := Btrv(OpCode, PosBlk, Fields, DBufferLen, Fields.KeyBuf, Key);
  134. end;
  135. ..............................................................................
  136.  
  137. The record variable itself should be used as the Btrieve data buffer
  138. parameter.  The data buffer length is reset to the file's record length before
  139. making the call.  The KeyBuf field of the MyFields record should be used in
  140. all Btrieve calls as the Btrieve key buffer.
  141.  
  142. As you can see, your BFile descendant will incorporate the universal Btrieve
  143. function call in a compact, elegant format.  Thanks to OOP and encapsulation,
  144. you are guaranteed that each descendant's own key and data buffers are used
  145. since each object has its private copy of the .BT method.
  146.  
  147. As an aside, the .BT function cannot be fully incorporated into the base BFile
  148. object since the base object has no data fields corresponding to your file's
  149. fields, and thus cannot contain a properly sized key buffer or data buffer.
  150. The override is necessary because neither the BFile object nor Btrieve itself
  151. know anything about fields or the appropriate size for your buffers.
  152.  
  153. In fact, to assure you don't call the .BT function of BFile directly, BFile.BT
  154. includes a call to TP6's Abstract method, which will crash your program with a
  155. runtime error unless you replace it with the function as defined and
  156. recommended here.
  157.  
  158. For your reference, the base BFile object is all of 698 bytes in size.  The
  159. BFile descendant used in EXAMPLE1.PAS, even with its additional methods and
  160. data fields, is only 748 bytes in size.  In contrast, the BFixed object is
  161. 5043 bytes in size, due to its inclusion of maximum size buffers for a fixed
  162. length record and maximum key length.
  163.  
  164. Assume a Btrieve file named 'DosFile'.  To initialize the BFile descendant
  165. defined above:
  166.  
  167.      MyFile := new(PMyObject, Init('DosFile', Normal));
  168.  
  169. The BFile constructor/initialization method will do a Btrieve open operation,
  170. using the open mode you provide as the second parameter to the Init call.
  171. After the open operation, a stat operation is performed.  The Btrieve
  172. filespec and key specs, retrieved by the stat call, are incorporated into
  173. BFile's data fields.  Those stats can then be read out at will simply by
  174. referencing the object's data fields in the following manner:
  175.  
  176.      MyFile^.Specs.PageSize, or MyFile^.NumRecs, etc.
  177.  
  178. The stat fields defined in the object include the number of keys, the total
  179. number of key segments, the page size, the number of records in the file
  180. when the INIT was performed, the file flags...in short, the complete Btrieve
  181. filespec.  All other stats can be derived from the object's data fields if
  182. needed.  If you ever need to refresh the stat data to refresh the record
  183. count, for example, you could simply:
  184.  
  185.      BStatus := MyFile^.Close;
  186.      MyFile := new(PMyObject, Init('DosFile', Normal));
  187.      HowMany := MyFile^.NumRecs;
  188.  
  189. Of course, you could always do a Stat call directly and deal with the
  190. results, but the BTP way is so EASY!!  MyFile^.NumRecs, a longint, is
  191. available immediately after the Init call.
  192.  
  193. Btrieve operations with BTP are performed by calling the BT function with just
  194. two parameters.  Here's a simple step next call:
  195.  
  196.   BStatus := MyFile^.BT(BStepNext, 2);
  197.  
  198.   (BStatus is a public integer var from this unit.  BStepNext is a public
  199.    constant.  A number of public constants are defined in this unit that can
  200.    help make your code more readable.)
  201.  
  202. There is some nominal overhead in the BFile object.  It allocates space for
  203. the maximum of 24 keys/segments.  This simply means a maximum of 368 bytes per
  204. open file would be wasted.  This maximum would apply to a standard Btrieve
  205. file that had just one unsegmented key.  The figure of 368 is derived as
  206. follows:
  207.  
  208.                  400 maximum bytes if the Btrieve max of 24 keys/
  209.                      segments is being used!!
  210.                -  16 bytes to hold the Btrieve file specs (stats)
  211.                -  16 bytes for the minimum required 1 Btrieve key spec
  212.                  ---
  213.                  368 bytes maximum possible waste
  214.  
  215. In addition, my convention of supplying the BFile descendant with a built-in
  216. key-buffer, sized to the length of the largest size key, could be overhead.
  217. I don't mind that a bit, and I don't think you should.
  218.  
  219. The benefits of referencing any open Btrieve file and its components or stats
  220. by name makes using the Btrieve record manager a snap.  Add to your descendant
  221. appropriate methods to get/set your fields and the key and data buffers and
  222. you'll be much more productive.
  223.  
  224. Your app's code is responsible for stuffing key values into the key buffer,
  225. getting or setting the data buffer, et al, as usual.  However, you gain the
  226. ease of accessing all Btrieve ops in a distinct shorthand, accessing those
  227. buffers and fields by name, and can rest assured that all parameters are
  228. passed and filled.  See EXAMPLE1.PAS for a full working model of this
  229. technique.
  230.  
  231. You will note that the examples sometimes break one of OOP's rules by
  232. accessing data fields directly.  In my own apps I will typically have at
  233. least a couple of additional methods in my BFile descendants for getting and
  234. setting the key buffers and data buffers...such as the following three:
  235.  
  236. (pass whatever string you want to stuff in the key buffer)
  237. procedure MyObject.SetKeyBuf(KeyBufString:string);
  238. var
  239.    Counter,
  240.    LengthKeyBufString : integer;
  241. begin
  242. LengthKeyBufString := length(KeyBufString);
  243. if LengthKeyBufString > 12 then     (replace '12' with length of your KeyBuf)
  244.    LengthKeyBufString := 12;
  245. for Counter := 1 to LengthKeyBufString do
  246.    Fields.KeyBuf[Counter] := KeyBufString[Counter]
  247. if LengthKeyBufString < 12 then
  248.    for Counter := (LengthKeyBufString+1) to 12 do
  249.       Fields.KeyBuf[Counter] := ' '; (pad with trailing blanks)
  250. end;
  251.  
  252. function MyObject.GetKeyBuf:string;
  253. begin
  254.    GetKeyBuf := Fields.KeyBuf;
  255. end;
  256.  
  257. function MyObject.GetDBuffer:string;
  258. begin
  259.    GetDBuffer := Fields.DBuffer;
  260. end;
  261.  
  262.  
  263. USING THE BFILEEXT OBJECT
  264. -------------------------
  265.  
  266. There were several assumptions coded into the BTP unit to make a working
  267. structure for the 'read' form of extended calls (see the declaration of
  268. data types for BFileExt in the unit's source code):
  269.  
  270.   1. That the required data buffer will never be more than 32767 bytes.
  271.   2. That values used for a filter's logic terms will never be longer
  272.      than 255 bytes.
  273.  
  274. Beyond these assumptions, which you can change if necessary by changing the
  275. unit's source or by defining your own descendants, BTP enables you to make any
  276. of the four extended get/step extended calls with ease.  The best examples
  277. of how to do so are in the example programs CRUNCH3.PAS and EXAMPLE2.PAS.
  278.  
  279. Admittedly, constructing outgoing buffers for Btrieve's extended calls can
  280. be frustrating.  Btrieve is hostile in this regard.  BTP reduces this
  281. drudgery to a couple of additional setup statements!
  282.  
  283. Recall that the outgoing buffer for these extended calls requires several
  284. data structures occupying contiguous bytes in the buffer:  a header, a
  285. filter, optional filter logic terms, an extractor, and at least one field
  286. extractor spec.  After initializing the data buffer, you must calculate
  287. the buffer length for the Btrieve call to be the larger of the outgoing or
  288. incoming buffers!  SHEESH!  The BTP way of dealing with this is as follows:
  289.  
  290.    1:  HEADER is handled internally.  Don't mess with it.  Yes, even setting
  291.        the buffer length in the header is handled internally.
  292.    2:  FILTER's fields MUST be assigned by your program.
  293.    3:  FILTERSPEC is a TP collection that may or may not hold any objects,
  294.        depending on your program's needs.
  295.    4:  EXTRACTOR's fields MUST be assigned by your program.
  296.    5:  EXTRACTORSPEC is a TP collection that must hold at least one object.
  297.    6:  EXTDBUFFER, the data buffer for these 4 extended calls, is handled
  298.        internally.  Don't mess with it, except to use it as shown below and in
  299.        EXAMPLE2.PAS when you override BFileExt.BTExt.
  300.    7.  Buffer structuring is totally transparent to your program.  Simply
  301.        override the BFileExt.BTExt function as shown, and you're there!
  302.        Believe me, this took some work!  The standard TP6 'ForEach' iterator
  303.        is used to take every item in the two collections and account for
  304.        them in structuring the outgoing buffer.
  305.  
  306. The BFileExt.BTExt method must be overridden, with the override calling the
  307. ancestor; i.e. your override must be of a standard form, and must *first* call
  308. BFileExt.BTExt.  This is because BFileExt.BTExt is what sets the buffer length
  309. and constructs the buffer for extended read calls.  This is in contrast to
  310. BFile.BT or BFileExt.BT, which must be overridden by REPLACING them.
  311.  
  312. Since Btrieve's extended calls permit use of filters based on field values
  313. or on a user (program) supplied value, there are two constructors for the
  314. logic term object: INITF (initialize to compare with field) and,
  315.                    INITV (initialize to compare with value).
  316. Refer to the BTP source code for a list of the parameters required by these
  317. two constructors.
  318.  
  319. I strongly urge you to review EXAMPLE2.PAS for a full working model of using
  320. extended calls with BTP.  The following is a skeletal example.
  321.  
  322. .............................................................................
  323. USES BTP;
  324.  
  325. TYPE
  326.    MyFields  = record
  327.                   case integer of
  328.                   1: (First   :array[1..10] of char;
  329.                       Last    :array[1..20] of char;
  330.                       KeyBuf  :array[1..20] of char); (sized to largest key)
  331.                   2: (DBuffer :array[1..30] of char); (sized to rec length)
  332.                   3: (Position:array[1..2] of word);
  333.                   end;
  334.    PMyObject = ^MyObject;
  335.    MyObject  = object(BFileExt)
  336.                   Fields: MyFields;
  337.                   function BT(OpCode, Key: integer): integer; virtual;
  338.                   function BTExt(OpCode, Key: integer): integer; virtual;
  339.                   end;
  340.  
  341. VAR
  342.    MyFile    = PMyObject;
  343.    Counter   = integer;
  344.    X         = string;
  345.    Value     = TCharArray;  {TCharArray is a data type in BTP...an array of
  346.                              255 chars.}
  347.  
  348. {NOTE that you can use these overrides verbatim for each and every descendant
  349.  if you follow the naming conventions outlined in this documentation and in
  350.  the example programs.}
  351.  
  352. function MyObject.BT(OpCode, Key: integer): integer;
  353. begin
  354.    DBufferLen := Specs.RecLen;
  355.    BT := Btrv(OpCode, PosBlk, Fields, DBufferLen, Fields.KeyBuf, Key);
  356. end;
  357.  
  358. function MyObject.BTExt(OpCode, Key: integer): integer;
  359. begin
  360.    BStatus := BFileExt.BTExt(OpCode, Key);   (MUST call ancestor method!!!)
  361.    BTExt   := Btrv(OpCode, PosBlk, ExtDBuffer^.Entire, DBufferLen,
  362.                    Fields.KeyBuf, Key);
  363. end;
  364.  
  365. BEGIN
  366.  
  367. MyFile := new(PMyObject, Init('Example', ReadOnly));
  368. {WHAM!  File 'Example' is now open in read only mode, with it's structure and
  369.  stats encapsulated in fields of the object.}
  370.  
  371. with MyFile^ do                       {Perform required data initializations.}
  372.    begin                              {This one code section takes care of   }
  373.    Filter.MaxSkip       := 50;        {initializing the filter and extractor.}
  374.    Filter.NumLogicTerms :=  2;
  375.    Extractor.NumRecords :=  5;
  376.    Extractor.NumFields  :=  1;
  377.    end;
  378.  
  379. {Now specify filter logic terms.  We'll setup filter to use 'Leon' (a Last
  380.  Name) as a value for filtering.}
  381. X := 'Leon';
  382. for Counter := 1 to length(X) do
  383.    Value[Counter] := X[Counter];
  384. for Counter := (length(X) + 1) to 255 do
  385.    Value[Counter] := ' ';                   {Pad the array w/trailing blanks.}
  386.  
  387. {Now use one statement to initialize the filter logic term object and insert
  388.  it into the FilterSpec collection.  Note use of the INITV constructor vs
  389.  the INITF constructor.}
  390.  
  391. with MyFile^.FilterSpec^ do
  392.    insert(new(PFilterSpec, InitV(BString, 20, 10, Equal, NextTermAnd, False,
  393.                                  Value)));
  394.  
  395. {Now setup another logic term to specify a first name search condition!}
  396. X := 'John';
  397. for Counter := 1 to length(X) do
  398.    Value[Counter] := X[Counter];
  399. for Counter := (length(X) + 1) to 255 do
  400.    Value[Counter] := ' ';
  401.  
  402. {Here's the statement to initialize the second filter logic term object and
  403.  insert it into the FilterSpec collection provided and initialized for you.}
  404.  
  405. with MyFile^.FilterSpec^ do
  406.    insert(new(PFilterSpec, InitV(BString, 10, 0, Equal, LastTerm, False,
  407.                                  Value)));
  408.  
  409. {Now, let's retrieve up to 5 matching records with a single call, extracting
  410.  such matching records in their entirety, not just particular fields.}
  411. with MyFile^.ExtractorSpec^ do
  412.    insert(new(PExtSpec, Init(MyFile^.Specs.RecLen, 0)));
  413.  
  414. {Oh, yea...need to establish a position before using these calls, so let's do
  415.  a Get First before the extended call!}
  416. BStatus := MyFile^.BT(BGetFirst, Zero);
  417.  
  418. {Now let's get on with it and make the extended call...}
  419. BStatus := MyFile^.BTExt(BGetNextExt, Zero);
  420.  
  421. {Take advantage of the data types and Pascal record structures provided to
  422.  see how many records were returned in this call.}
  423. writeln('Number of records returned with name ''John Leon'' is ',
  424.         MyFile^.ExtDBuffer^.NumRecs);
  425.  
  426. {Don't forget to close the file and dispose of the dynamic object.  The
  427.  object's Done destructor will also dispose of the collections for you.}
  428.  
  429. BStatus := MyFile^.Close;
  430. dispose(MyFile, Done);
  431.  
  432. END.
  433.  
  434. .............................................................................
  435.  
  436. This should be enough to get you started using BTP with both non-extended
  437. and extended calls.  Again, refer to the unit's source code and to the
  438. example programs for a fuller understanding of the BTP structures and
  439. technique.
  440.  
  441.  
  442. ABOUT THE EXAMPLE PROGRAMS
  443. --------------------------
  444.  
  445. Refer to the README.1ST file for a list and description of the example
  446. programs.  They should be extremely helpful in getting you up to speed with
  447. the BTP product.  Several of them are utility programs you can use "out of
  448. the box"!
  449.  
  450.  
  451. PARTING SHOTS
  452. -------------
  453.  
  454. ENJOY BTP PROGRAMMING!  BTP is not free...it is shareware.  Yours with no
  455. obligation for a 30 day trial period, you are expected to remit the $25
  456. registration fee if you use BTP.  Please refer to the README.1ST file for
  457. license terms and remittance instructions.
  458.  
  459. The most current version of BTP can usually be found in 3 places on Compu-
  460. serve if not available to you through your favorite BBS or shareware catalog:
  461.  
  462.    Borland's Programmer's Forum (go BPROGA), in download library 1
  463.    IBM Programmer's Forum (go IBMPRO), in download library 0 (new uploads) or
  464.                                           download library 5 (other langs)
  465.    Novell Netwire (go NOVA), download library 17 or 14.
  466.    
  467.  
  468. Even if you choose not to register your copy of BTP, I would like to hear
  469. from you about your experiences with and reaction to using this unit.  In
  470. addition, of course, constructive criticism and suggestions are always
  471. welcome.
  472.  
  473.    John C. Leon
  474.    3807 Wood Gardens Court
  475.    Kingwood, TX  77339
  476.  
  477.    713-359-3641 (residence)
  478.    CIS #72426,2077
  479.