March 1995 The CS-libraries Version: 1.6.b Theo van den Bout P.O. Box 3303 2280 GH Rijswijk The Netherlands Copyright (c) 1994,1995 by Combis, the Netherlands. All Rights Reserved. 1 Contents 1 Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1 Shareware. . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Contacting the Author. . . . . . . . . . . . . . . . . . . . . . 2.3 Registering. . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Legal Matters. . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Disclaimer . . . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Trademarks . . . . . . . . . . . . . . . . . . . . . . . . 3 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Debugging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Runtime Libraries. . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1 Using the libraries. . . . . . . . . . . . . . . . . . . . . . . 6.2 Compiler options . . . . . . . . . . . . . . . . . . . . . . . . 7 General Definitions & Functions. . . . . . . . . . . . . . . . . . . . 7.1 Portability. . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Messages and errors. . . . . . . . . . . . . . . . . . . . . . . 7.3 Temporary Files. . . . . . . . . . . . . . . . . . . . . . . . . 8 Buffering. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 PAGE-Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.3 Storing data in the header-page. . . . . . . . . . . . . . . . . 10 TBASE-class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 10.2 Using TBASE . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3 Creating a Database . . . . . . . . . . . . . . . . . . . . . . 10.4 Opening . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.5 Closing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.6 Appending Records . . . . . . . . . . . . . . . . . . . . . . . 10.7 Deleting Records. . . . . . . . . . . . . . . . . . . . . . . . 10.8 Advanced Topics . . . . . . . . . . . . . . . . . . . . . . . . 10.8.1 Page Utilization. . . . . . . . . . . . . . . . . . . . . 10.8.2 Locating Records. . . . . . . . . . . . . . . . . . . . . 10.9 Miscellaneous functions . . . . . . . . . . . . . . . . . . . . 10.10 Functions in alphabetical order. . . . . . . . . . . . . . . . 11 BTREE-class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 BTREEx Classes. . . . . . . . . . . . . . . . . . . . . . . . . 11.3 Multiple Keys . . . . . . . . . . . . . . . . . . . . . . . . . 11.4 Current Pointer . . . . . . . . . . . . . . . . . . . . . . . . 11.5 Using Btrees. . . . . . . . . . . . . . . . . . . . . . . . . . 11.5.1 Creating. . . . . . . . . . . . . . . . . . . . . . . . . 11.5.2 Opening . . . . . . . . . . . . . . . . . . . . . . . . . 11.5.3 Inserting . . . . . . . . . . . . . . . . . . . . . . . . 11.5.4 Searching . . . . . . . . . . . . . . . . . . . . . . . . 11.5.5 Current . . . . . . . . . . . . . . . . . . . . . . . . . 11.5.6 Deleting. . . . . . . . . . . . . . . . . . . . . . . . . 11.5.7 Closing . . . . . . . . . . . . . . . . . . . . . . . . . 11.6 Functions in alphabetical order.. . . . . . . . . . . . . . . . 12 CSDBGEN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 12.2 Overview. . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.3 Features. . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . 12.5 Definition file . . . . . . . . . . . . . . . . . . . . . . . . 12.6 Tokenizing. . . . . . . . . . . . . . . . . . . . . . . . . . . 12.6.1 How does it work? . . . . . . . . . . . . . . . . . . . . 12.7 When is a substring indexed?. . . . . . . . . . . . . . . . . . 12.8 Export to dBASE . . . . . . . . . . . . . . . . . . . . . . . . 12.9 Exporting/Importing to/from ASCII . . . . . . . . . . . . . . . 12.10 Starting a new database. . . . . . . . . . . . . . . . . . . . 12.11 Opening a database . . . . . . . . . . . . . . . . . . . . . . 12.12 Current Record . . . . . . . . . . . . . . . . . . . . . . . . 12.13 Accessing fields . . . . . . . . . . . . . . . . . . . . . . . 12.14 DATE fields. . . . . . . . . . . . . . . . . . . . . . . . . . 12.15 Changing the record layout.. . . . . . . . . . . . . . . . . . 12.16 Member functions in alphabetical order . . . . . . . . . . . . 12.17 Warning. . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.18 Example. . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 VRAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 13.2 Creating. . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3 Opening & Closing . . . . . . . . . . . . . . . . . . . . . . . 13.4 VRAM Pointers . . . . . . . . . . . . . . . . . . . . . . . . . 13.5 Fragmentation . . . . . . . . . . . . . . . . . . . . . . . . . 13.6 Root. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.7 Functions in Alphabetical order.. . . . . . . . . . . . . . . . 14 VBASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 14.2 Using VBASE.. . . . . . . . . . . . . . . . . . . . . . . . . . 14.3 Relocating records. . . . . . . . . . . . . . . . . . . . . . . 14.4 Limitations.. . . . . . . . . . . . . . . . . . . . . . . . . . 14.5 Functions in alphabetical order.. . . . . . . . . . . . . . . . 15 VBAXE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 15.2 Working.. . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.3 Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.4 Prototypes. . . . . . . . . . . . . . . . . . . . . . . . . . . 16 CSDIR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 CSINFO. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 CSERROR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 CSADD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.1 Source. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.2 Openings screen . . . . . . . . . . . . . . . . . . . . . . . . 19.3 Features. . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 CSTOOLS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 21 CSKEYS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.1 CSKEYS.exe. . . . . . . . . . . . . . . . . . . . . . . . . . . 22 HEAP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.1 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.2 When to use it? . . . . . . . . . . . . . . . . . . . . . . . . 22.3 Using HEAP. . . . . . . . . . . . . . . . . . . . . . . . . . . 22.4 Functions in alphabetical order.. . . . . . . . . . . . . . . . 23 Alloc-Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 23.2 Replacements. . . . . . . . . . . . . . . . . . . . . . . . . . 23.3 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23.4 Memory Leaks. . . . . . . . . . . . . . . . . . . . . . . . . . 24 CSEDSTR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 CSWINDOWS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25.1 Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . 25.2 General Information . . . . . . . . . . . . . . . . . . . . . . 25.3 The C++ version, the class WINDOW . . . . . . . . . . . . . . . 25.3.1 Syntax of the MEMBER functions. . . . . . . . . . . . . . 25.4 The C version . . . . . . . . . . . . . . . . . . . . . . . . . 25.4.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . 25.5 Syntax of the C functions . . . . . . . . . . . . . . . . . . . 25.6 Working within a Window . . . . . . . . . . . . . . . . . . . . 25.7 File Browsing . . . . . . . . . . . . . . . . . . . . . . . . . 26 CSMENU. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26.1 General Information . . . . . . . . . . . . . . . . . . . . . . 26.2 Defining a menu . . . . . . . . . . . . . . . . . . . . . . . . 26.3 Connecting menus. . . . . . . . . . . . . . . . . . . . . . . . 26.4 Displaying the menu . . . . . . . . . . . . . . . . . . . . . . 26.5 Using menus . . . . . . . . . . . . . . . . . . . . . . . . . . 26.6 Removing the menu . . . . . . . . . . . . . . . . . . . . . . . 27 CSPANEL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27.1 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27.2 General Information . . . . . . . . . . . . . . . . . . . . . . 27.3 Public member functions . . . . . . . . . . . . . . . . . . . . 27.4 Dates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27.5 Fields. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27.6 Using the panels. . . . . . . . . . . . . . . . . . . . . . . . 27.7 Data validation . . . . . . . . . . . . . . . . . . . . . . . . 27.7.1 Min and Max . . . . . . . . . . . . . . . . . . . . . . . 27.7.2 Reset Max & Min . . . . . . . . . . . . . . . . . . . . . 27.7.3 Templates . . . . . . . . . . . . . . . . . . . . . . . . 28 Registration Form . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Preface This is a shareware package consisting mainly of two distinct libraries. The libraries are to be used with Borland C++ 3.1 or later. Both MS-DOS and Windows applications are supported. The libraries are intended for programmers who need some kind of database in their application but are reluctant to use any of the vast DBMS's. The supplied classes are doing all their IO themselves, apart from the OS, no other software is needed. One library concentrates on supplying a user-interface for the non- Windows programmer, the other library supplies the classes necessary to build database applications. An application generator is used to produce the source for the more complicated databases with indexes and fields. 2.1 Shareware This software is distributed as 'Shareware' which means you are free to copy and share these files. It is a fully functioning version, but it does NOT include the complete package. 2.2 Contacting the Author You can reach me, preferably, by E-mail. The address is: T.P.vandenBout@et.tudelft.nl. If you don't have E-mail access you can reach me by traditional mail: Theo van den Bout P.O. Box 3303 2280 GH Rijswijk The Netherlands For urgent matters only, phone me at +31703960172. Please remember, it is GMT +100 over here. I have serious problems with being woken in the middle of the night! 2.3 Registering When you send f 200.- (200 Dutch guilders) or $ 125 (125 American Dollars) you will become a 'registered user'. This means you will receive - the latest version - all the libraries - support. - a nice manual. Send money, check or money order to: Combis P.O. Box 3303 2280 GH Rijswijk The Netherlands You will find a registration form at the end of this documentation. 2.4 Legal Matters 2.4.1 Disclaimer EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDER AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL THE COPYRIGHT HOLDER BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 2.4.2 Trademarks IBM is a registered trademark of International Business Machines Corporation. MS-DOS and Windows are registered trademarks of Microsoft Corporation. Borland C/C++ and dBASE are registered trademarks of Borland International Inc. Part One The next part of the documentation gives an overview of this package. It will also discuss the PAGE- and the BUFFER-class from which all database classes are derived. Some other topics of general interest are covered too. 3 Introduction This package mainly consists of two almost independent libraries. The library called CSDB, is the far most important one. The other one, CSA, is kind-of 'thrown in for free'. Despite this, it contains important general purpose functions heavily used by the CSDB library. The main purpose of this library is to provide a C++ programmer with the utilities to incorporate databases in its applications without being forced to use one of the huge DBMS's currently on the market. The CSDB-library contains the classes needed to fulfil this purpose. Therefore, the emphasis in this documentation is on the CSDB library. The CSA library is intended to provide the non-Windows programmer with the tools to easily build a user interface. The library therefore contains classes for menu's, forms and windows. As an example an address-book type of application is included. This should give a quick impression of the purpose and capabilities of the classes. Although this package is shareware, it still contains a fully functioning version. However, not all the libraries are included. What is shipped is the library with the debug-version of the large memory model. As explained further on, there are two versions of each library. One containing a lot of code to trap errors, and a second one optimized for speed. The library in this shareware package will therefore produce somewhat larger and slower applications then needed. Please, do keep this in mind. 4 Overview This page will try to give a quick overview of the contents of this package. The CSDB-library contains (among others) the following classes: TBASE: A class for reading and wrtiting records. BTREE: A btree+ to be used as an index. VRAM: A 'database' organized as a heap. VBASE: Variable length records. VBAXE: As VBASE but for very large databases. The CSA-library contains general-purpose functions and classes to build a character oriented user-interface. Among others, the following classes are included: WINDOW: Text windows. MENU: A menu system. PANEL: Data entry screens. HEAP: To efficiently allocate many small blocks from the heap. Command line utilities (DOS): CSDIR: Lists the databases in a directory. CSINFO: Displays information about a database. CSDBGEN: Important program-generator. CSERROR: Utility to convert the error file to C++ source. CSKEYS: Displays the return value of the cskey() function. CSMALLOC: Tests the allocation log for memory leaks. Documentation: CSDB.txt: This documentation in ASCII format. Examples: CSADDRESS: A nice address database (DOS). CSMENDEM: Demonstration of the MENU class. CSPANDEM: Demonstration of the PANEL class. 5 Debugging Of each library two versions exist, one to be used during debugging and one intended for normal 'production'. The difference is that in the debug-version a lot more tests are done, and so many more errors are reported. The idea is to use the debug version during development and recompile with the production version when ready. The debug version is identical to the production version, but with additional tests. The 'working code' is 100% identical. This means there are no subtle differences between the two versions. The production version however can be substantial faster, up to two times, depending on the circumstances. To give an example: The TBASE class has functions to read a record from the database. For this function to operate properly, the class/database needs to be 'opened'. In the debug version this is tested with every call to the read function. In the production version this is never tested. Note that in a decently written and tested application this error should not occur. Errors with the open function should be trapped much earlier. There are many more errors like this. Errors that should not occur when the application is tested and (almost) ready but which can easily emerge in the development stage. 6 Runtime Libraries The libraries are to be used with the Borland C++ compilers. Versions exist for both MS-DOS and Windows. With MS-DOS three memory models are supported: compact, large and huge. These are the memory models which use far data. The same applies for MS-Windows but with the exception of the huge memory model. Windows doesn't allow that. ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Name ³Operating ³ Library ³ Memory ³ Type º º ³System ³ ³ Model ³ º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDACP ³Dos ³ CSA ³ Compact ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDACD ³Dos ³ CSA ³ Compact ³ Debug º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDALP ³Dos ³ CSA ³ Large ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDALD ³Dos ³ CSA ³ Large ³ Debug º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDAHP ³Dos ³ CSA ³ Huge ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDAHD ³Dos ³ CSA ³ Huge ³ Debug º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Name ³Operating ³ Library ³ Memory ³ Type º º ³System ³ ³ Model ³ º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWACP ³Windows ³ CSA ³ Compact ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWACD ³Windows ³ CSA ³ Compact ³ Debug º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWALP ³Windows ³ CSA ³ Large ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWALD ³Windows ³ CSA ³ Large ³ Debug º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Name ³Operating ³ Library ³ Memory ³ Type º º ³System ³ ³ Model ³ º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDDCP ³Dos ³ CSDB ³ Compact ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDDCD ³Dos ³ CSDB ³ Compact ³ Debug º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDDLP ³Dos ³ CSDB ³ Large ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDDLD ³Dos ³ CSDB ³ Large ³ Debug º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDDHP ³Dos ³ CSDB ³ Huge ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSDDHD ³Dos ³ CSDB ³ Huge ³ Debug º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Name ³Operating ³ Library ³ Memory ³ Type º º ³System ³ ³ Model ³ º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWDCP ³Windows ³ CSDB ³ Compact ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWDCD ³Windows ³ CSDB ³ Compact ³ Debug º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWDLP ³Windows ³ CSDB ³ Large ³ Production º ÇÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º CSWDLD ³Windows ³ CSDB ³ Large ³ Debug º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍͼ Of course you have noticed there is a system in these names: csOLMT.lib with: O: Operating System D is DOS, W is Windows L: Library A is CSA D is CSDB M: Memory model C is Compact L is Large H is Huge T: Type P is Production D is Debug In the shareware distribution only two libraries are found: CSD.lib: For Dos. This is a combination of CSDDLD.lib and CSDALD.lib. CSW.lib: For Windows. This is a combination of CSWDLD.lib and CSWALD.lib. 6.1 Using the libraries Unless you are using only the classes from the CSA collection you always have to link in two libraries. Example: Suppose you start developing in the large memory model under DOS. Link CSDDLD.lib and CSDALD.lib. It is best to link CSDDLD.lib first. When the application is appropriately debugged, it is time to start using the CSDDLP.lib and the CSDALP.lib. After that, you may consider porting to Windows. Assuming you can skip the debugging phase there, link the CSWDLP.lib and the CSWALP.lib. Users of the shareware distribution have far less choice: use CSD.lib for DOS and CSW.lib for windows. 6.2 Compiler options The production library is compiled with: - No tests for stack overflow. - Optimized for speed. - No overlays. - 80286 instructions. - Signed characters with BC 3.1 - 'Undefined' characters with BC 4.x. - Byte alignment. The debug library is compiled with tests for stack overflow. Apart from that it's identical. (Using the same compiler options that is.) The Windows' version is compiled with 80386 instructions. Make sure you are using the right type of characters. If not, you will experience linking problems. Don't use word-alignment. 7 General Definitions & Functions 7.1 Portability A series of typedefs is used to create portable variable types. S8: Singed 8 bit, (singed char) U8: Unsigned 8 bit, (unsigned char) S16: Singed 16 bit (int) U16: Unsigned 16 bit (unsigned int ) S32: Singed 32 bit (long) U32: Unsigned 32 bit (unsigned long) uchar unsigned char schar signed char CSCHAR char However, on many occasions (particularly function returns) the range of the variable is not all that much important. In these cases integers are used because that's normally the fastest. 7.2 Messages and errors A special set of functions is used to display errors, messages and the alike. To avoid confusion: these are 'normal' C-type functions, not public functions of some message class. All these functions eventually display their messages through a call to csmess_disp(char *). By default (under DOS), this function writes all the messages to the console by using the standard cputs() function. With WINDOWS, a standard message box is called. Fortunate, this function can easily be altered. Before being displayed by the csmess_disp() function, all the messages are converted into a single string. This makes changing the message function very easy. Only a single function, which accepts a character pointer, needs to be supplied. The next function is intended to do that: void csmess_set_fun( void (* fun)(char *)); // Example (Dos): void display(char *s) { // This function is going to be used // To display messages. printf("%s",s); } void main(void) { csmess_set_fun(display); // From now on, all the messages are // displayed through a call to the // 'display()' function. } To restore the default, the next function can be used: void csmess_reset_fun(void); // Example: void main(void) { csmess_reset_fun(); // Restores the default // Display function. } void csmess_off(void); With this function, messages can be suppressed. Whether you are using the standard message function or has it replaced with your own, after a call to this function no message will be displayed. void csmess_on(void); To be used in conjunction with the csmess_off() function. After a call to 'csmess_on()' messages will be displayed again. int csmess_onoff(void); Returns TRUE if message displaying is switched on. FALSE otherwise. void csmess_onoff(int sw); If called with sw unequal zero, messages will be displayed. Otherwise not. 7.3 Temporary Files. Temporary files are created through the use of the cstmpname() function, discussed in the CSTOOLS chapter. This means, the environment variables TEMP and TMP are checked to determine which subdirectory is going to be used. Temporary files can be as large as the databases they are belonging to. So, make sure the environment variables don' t point to some small ram-disk or an insufficient large partition. 8 Buffering All the disk IO is done by a very solid buffer-system. All the database classes have a function which makes it possible to control the amount of memory used for buffering. This amount is not allocated right from the start but is interpreted as a maximum. This means that buffers are allocated on the fly, up to this limit. The advantage is that the maximum may never be reached, saving valuable memory for other purposes. Of course there is also a drawback. The problem is that there is no way the classes can predict how much memory needs to be reserved for the remaining part of the program. The buffering-system itself will stop allocating memory when the heap is exhausted, but if dynamic memory allocation is used somewhere further on, the program may still terminate with a message of the type 'out of memory'. It should be noted that this is only a problem with the MS-DOS operating system. Any other (real) operating system uses virtual memory which makes sure that your program will work with any reasonable assumption about the available amount of memory. For performance reasons, however, it is better not to rely on virtual memory. Let the classes do the buffering, not the operating system. What all this means is that with MS-DOS you have to be careful about using a large amount of ram for buffering. 9 PAGE-Class 9.1 Introduction The PAGE-class constitutes a kind of 'foundation' for most of the other classes in the CSDB library. It is derived from a class 'BUFF' which takes care of the required buffering. (Described in the previous chapter.) The idea is to do disk IO in chunks of 2 Kb. This is close to the optimal size for the average harddisk. These blocks are kept aligned with the sectors of the harddisk, which improves speed considerablely. A harddisk always reads an entire sector, even if you only need, let's say, 10 bytes. Things become even worse if the 10 bytes you are requesting just happen to cross a sector boundary. In that case your harddisk will read 2 entire sectors. Assuming a sector is 1024 bytes, this means that 2*1024=2048 bytes are read just to obtain your 10 bytes! To avoid this kind of inefficiency the PAGE-class does its disk IO in pages of 2048 bytes while making sure every page is aligned with the harddisk sectors. This also means that the indispensable file-header has to be at least one sector. To avoid complications, a file-header is used which has the same size as a page, 2048 bytes by default. It should be noted that this entire scene is undone by using a disk compression utility, like double space, stacker and the alike. Therefore, if you are concerned about performance, it is better not to use these utilities. More over, a disk compressor will slow down your application considerablely when several files are used heavily 'at the same time'. This situation will almost inevitably arise with any serious application which uses more then one database, or even a single database with many indexes. 9.2 Background One of the special features implemented in the PAGE-class is the 'background()' function. This function is intended to make good use of the idle time waiting for the user's input. It is guaranteed to return in a very short period of time, doing at most one disk IO with every call. By result you can write code like: while(!kbhit()) CLASS.background(); E.g. the background() function supplied by the PAGE class writes 'dirty' buffers back to disk. This is done one-by-one. Each call will write at most one buffer. Because of this, the application user will hardly notice anything, while in the future the application doesn't have to spend time writing buffers back to disk. The function will return a value greater then zero if something has been done and zero otherwise. After some time the class runs out of things to do and it becomes pointless to spend CPU-time on calls to the background() function. By testing the return value, this can be avoided. In some derived classes, the background() function is overloaded to do additional house keeping. 9.3 Storing data in the header-page As explained above, the header page is quite large. This page is used to store al kind of important variables. However, there is still much space left. Of the 2048 bytes only about 170 are used. An application using databases is like to have some variables of his own which need to be maintained between close/open sequences. It seems the remaining space in the header page is a convenient place store such data. This can save you an additional configuration file and all the error trapping involved. To aid in this, three functions are made public: int data_2_header(void * ptr,U16 length); int header_2_data(void * ptr,U16 length); U16 max_data_in_header(void); U16 max_data_in_header(void); This function returns the maximum number of bytes which will still fit in the header page. This is simply the size of the header-page minus what is used to store the variables of the class. The class needs to be open. int data_2_header(void * buffer,U16 length); Copies data from buffer 'buffer' to the empty space in the header page. The variable 'length' indicates the number of bytes to be copied. This figure is not stored anywhere. It is the programmers' responsibility to retrieve the right number of bytes later on. The class needs to be open. int header_2_data(void * buffer,U16 length); The counterpart of the previous function. This function copies 'length' number of bytes from the header page to 'buffer'. The class needs to be open. Part Two Part Two of the documentation will explain how this library can be used to build traditional relational databases. To do so, it uses a TBASE class to store records and a BTREE class for indexes. A program generator, CSDBGEN, is discussed which 'automates' the process of building more complex databases out of TBASE and BTREE. These two classes can also be used seperately. In particular the BTREE class is very useful. It is really easy expandable and can be tailored to a specific purpose by supplying one single function. Simple databases with only one index can be build with just the BTREE class. 10 TBASE-class 10.1 Introduction The TBASE class is intended as a simple, fast way to access records on disk. It assumes a fixed record size and does its IO on a record-by- record basis (contrary to field-by-field). This means: 1) TBASE is unaware of something like 'fields'. The idea is to use a C structure as record and to do all the accessing of fields with the standard C operators. This approach is undoubtedly faster then supporting access on a field-by-field basis as done by dBASE. 2) No indexes. TBASE just reads or writes records, nothing else. NOTE: From this it is clear that with the TBASE class alone no decent database application can be build. Therefore, a separate BTREE class is supplied which can be used as an index. To glue it all together, a program generator, CSDBGEN, is available. Because C isn't particularly bright in handling strings and date's, the program generator takes care of that too. 10.2 Using TBASE The next small example gives an impression of how to use the class. As can be seen from this example, there is no 'record pointer' as in dBASE. The functions to read and write a record, simply take an additional parameter indicating the record number. // A very simple example. # include "CSTBASE.H" void main(void) { typedef struct { char name[20]; // The field 'name' char street[40]; // The field 'street' long salary; // The field 'salary' // All the other fields you may require. }record; // The record layout is now defined. TBASE db; record rec; db.open("demo.dbf",110); // Assuming the file is already created. // Use 110 kB for buffering. db.read_rec(9,&rec); // Read record number 9 into // variable 'rec'. rec.salary=0; // Change salary. db.write_rec(9,&rec); // Write the record back to position 9. // (Any other position is also possible.) db.close(); // Is also done automatically // by the class destructor. } 10.3 Creating a Database Before a database can be used it has to be 'created'. This is done through a call to the 'define()' function. Of course this is needed only once. Because TBASE doesn't use fields, the function takes only two parameters: the filename of the database, and the record size. Syntax: int define(char * name,U16 reclen); // This example creates a database 'demo.dbf'. #include "CSTBASE.H" void main(void) { typedef struct { char name[20]; char street[40]; char city[25]; } record; TBASE db; db.define("demo.dbf",sizeof(record)); } 10.4 Opening Before a record can be read, the database has to be opened through a call to the open() function. This open() function also takes a parameter indicating the amount of memory to be used for buffering. The memory for the buffers is NOT allocated at the moment of the call to open(), but during the use of the database. Memory is allocated when needed, up to this maximum. As explained in the chapter about buffering, using up too much memory for buffering is dangerous on an Operating System like MS-DOS with no virtual memory. Syntax: int open(char *name, S16 kb=32); // Example: // Opening the existing database 'demo.dbf' with 40 kB for buffers. #include "CSTBASE.H" void main(void) { TBASE db; db.open("demo.dbf",40); } 10.5 Closing Closing the database involves writing all the buffered data back to disk and freeing all allocated memory. The close() function is intended for this purpose. If the close() function is not explicitly called in the application, the class destructor will call it. Because there can be a long interval between the last time the database is used and the moment where the destructor is reached, it still makes sense to call the close() function 'by hand'. Syntax: int close(void); // Example: #include "CSTBASE.H" void main(void) { TBASE db; db.open("demo.dbf",40); db.close(); } 10.6 Appending Records A special function is needed to add a record to a database: the append_rec() function. Note: The write_rec() can only overwrite an already existing record. Syntax: long append_rec(void *data); 'data' is a pointer to a record. Syntax: long append_rec(void); This function can be used to add a record to the database without instantly filling it with a record. For the time being, this record will contain 'garbage'. // Example: #include "CSTBASE.H" void main(void) { typedef struct { char name[20]; char street[40]; char city[25]; } record; TBASE db; record rec; db.define("demo.dbf",sizeof(record)); //Create new database db.open("demo.dbf",40); strcpy(rec.name,"J.Q. Querlis "); strcpy(rec.street,"Avenue 120"); strcpy(rec.city,"Bombay"); db.append_rec(&rec); // The database now contains 1 record. db.close(); } 10.7 Deleting Records Deleting a record cannot be accomplished instantaneously. A 'delete bit' is used to distinguish deleted records. Deleting a record by setting the 'delete bit' doesn't alter much. E.g. record 9 remains record 9 if you delete record 8. The function 'is_delet()' has to be called to detect whether-or-not a record is 'deleted'. The 'pack()' function can be used to physically remove all the deleted records from the file. int is_delet(long r); Returns 0 if the record 'r' is not deleted, and 1 otherwise. void delet(long r); Sets the delete bit for record 'r'. void undelet(long r ); Resets the delete bit for record 'r'. void pack(void); Removes all the records with the delete bit set from the database. This is done without the use of temporary files. 10.8 Advanced Topics The next paragraph can be skipped by first-time readers. 10.8.1 Page Utilization The TBASE class does its IO in pages of 2 kB. It fits an integer number of records on these pages. This approach can lead to a large chunk of unused space on the pages, particularly if you are using large records. On average the slack will be a half record, but if you have a record of 1100 bytes almost 1 kB will be wasted! Solution: This waste of disk space can be avoided by using pages which have the same size as the record. This means that the pages will no longer be aligned with the sectors of your harddisk! The function to accomplish this is: smallest_page(). void smallest_page(void); The function has to be called before the define() function. Because this changes the entire layout of the database file, this cannot be altered once the database is created. // Example: #include "CSTBASE.H" void main(void) { typedef struct { char name[20]; char street[40]; char city[25]; } record; TBASE db; db.smallest_page(); //Before the define! db.define("demo.dbf",sizeof(record)); } 10.8.2 Locating Records In the examples given so far, a record is first read into a local variable and, after being altered, written back to disk. It seems there is room for improvement here. After the record is copied into the local variable it is in memory TWICE, once in the variable and another time in the database buffers. If you know what you are doing, a significant performance increase can be gained from obtaining a pointer directly into the buffer system. The 'locate_rec()' function does just that. When you are working in the database buffers through a pointer, there is no way the buffer system can tell if you are altering anything. Therefore, it's the programmers' responsibility to indicate whether or not modifications are going to take place. char *locate_rec(long rec); This function returns a pointer to record 'rec'. It assumes that no alterations are going to take place. This means that the buffer is not written back to disk! char *locate_rec_d(long rec); The additional '_d' stands for Dirty buffer. The function returns a pointer to record 'rec'. It is assumed alterations ARE going to take place. The buffer is therefore written back to disk when space is needed to store another page. IMPORTANT!! The locate functions return a pointer directly into the buffer system. Nothing less and nothing more. Any member function of the same class instance which MAY cause disk IO, can therefore alter the contents of the buffers, making your pointer 'point' to an entirely different record! When using these functions, it is highly advisable to do all the reading or writing to the record before calling any other TBASE member function. Special care should be taken in using the 'background()' function. If you are not sure you fully understand this, don't use the locate functions. // Example, of the locate_ function. #include "CSTBASE.H" void main(void) { typedef struct { char name[20]; int age; } record; TBASE db; record *rec; db.define("demo.dbf",sizeof(record)); db.open("demo.dbf"); // Use default 32 kB for buffers. for(int i=1;i<=12;i++) db.append_rec(); // Append 12 records. rec=(record *)db.locate_rec_d(7); // Obtain pointer to record 7. // '_d' because we will 'write'. rec->age=34; // That's all it takes to make an // alteration. No need for a // 'write' function. db.close(); // Not strictly necessary. } 10.9 Miscellaneous functions long numrec(void); Returns the number of records currently in the database. U16 lengthrec(void); The size of the record in bytes. 10.10 Functions in alphabetical order. Function prototypes are in 'cstbase.h'. S32 append_rec(void *data); Append a record to the database. The newly created record is filled with the data from buffer 'data'. The function returns the number of the new record (which is equal to the number of records in the database). S32 append_rec(void); Same as the previous function, only this time the new record is filled with binary zero's. int close(void); Closes the database. If the database is already closed, nothing happens. TRUE is returned on success, FALSE otherwise. int define(char *name,U16 reclen); Creates a TBASE file named 'name'. The parameter 'reclen' indicates the size of the records. TRUE is returned on success, FALSE otherwise. void delet(S32 rec); Sets the delete bit of record 'rec'. int empty(void); Removes all the records from the database. Afterwards there will be zero records left, but the database will still be open. TRUE is returned on success, FALSE otherwise. int is_delet(S32 rec); Returns the value of the delete bit of record 'rec'. TRUE means the delete bit is set, FALSE means the bit is not set. U16 lengthrec(void); Returns the length of a record. Because TBASE works with fixed length records, this value is the same for all records. It's the same value used in the call to define(). char *locate_rec(S32 rec); char *locate_rec_d(S32 rec); Functions to return a pointer to record 'rec' directly into the buffer system. Please read the paragraph about this topic before using these functions. S32 numrec(void); Returns the number of records currently in the database. Whether or not a record is marked for deletion makes no difference. int open(char *name,S16 kb=32); Opens the existing database 'name' while using 'kb' Kb ram for buffering. TRUE is returned on success, FALSE otherwise. int open(void); Returns TRUE if the database is open, FALSE otherwise. int pack(void); Removes all the records with the delete bit set from the database. This is doen without the use of temporary files. TRUE is returned on success, FALSE otherwise. void read_rec(S32 rec, void *buff); Copies the contents of record 'rec' into buffer 'buff'. int save(void); Writes all buffered data back to disk. The database header block is also updated. The database remains open. TRUE is returned on success, FALSE otherwise. void set_delet(S32 rec,int ToF); Changes the value of the delet bit of record 'rec'. If 'ToF' is TRUE, the delete bit is set, otherwise it is reset. void smallest_page(void); Set the page size to its smallest possible value. That is, a page will have the same size as a record. This means pages will no longer be alligned with the harddisk sectors. The function has to be called before the define() function. It changes the entire layout of the database file so it cannot be changed once the file is created. void undelet(S32 rec); Resets the delete bit of record 'rec'. void write_rec(S32 rec, void *buff); Overwrites the contents of record 'rec' the data from buffer 'buff'. Record 'rec' needs to exist, the function connot be used to append a record. 11 BTREE-class 11.1 Introduction A btree is a system, developed several decades ago, to store data in some predetermined order. The btree is capable of maintaining this order even under insertions and deletions. It is possible to use btrees for data in ram, but they were designed (and so optimized) to work with data on disk. Btrees are capable of locating, inserting or deleting a specific record with only a few disk-IOs, even when they have become very big. Of course there is a price to be payed for this: the disk space occupied by the btree is not fully utilized. Worst case, only 50% of the space is used. Fortunate, in the average case 75% is used. Btrees are convenient for indexes on a database. Of a record in the main database, the key field and the corresponding record number are stored together in the btree. When a certain record is needed, the btree is capable of quickly (without much disk IO) locating the required key value because the btree keeps the key values sorted. When the key field is located in the btree, it is instantly clear which record from the main database has to be read from disk. There are several 'flavours' of btrees. The one implemented in this library is known as a 'btree+'. 11.2 BTREEx Classes Unfortunate, it's a little cumbersome to use one BTREE class for every type of data. Therefore, there are several minor variations on the main BTREE class to account for the different variable types supported by C. Each type has its own class. Classes: BTREEb For binary data. BTREEi For integers. BTREEl For longs. BTREEc For characters. BTREEf For floats. BTREEd For doubles. BTREEa For ASCII data. (Strings) All these classes are derived from the BTREE class. Mainly, they differ only in one function. This is the function needed to compare keys. You can easily define a new BTREE class for new type of variable. The only thing to do is define a int t_key(void *a,void *b) function which returns: >0 if a>b 0 if a==b <0 if anumber-((record *)b)->number; } virtual int class_ID(void) { return -100; } }; That's all! The value '-100' in the class_ID() function is not all that important. Its purpose is to give other library functions the opportunity to distinguish between the classes. The value just has to be different from any value the other classes have. This can easily be accomplished by choosing a negative number. 11.3 Multiple Keys Because of its nature you would expect a 'key' to appear only once in a btree. However, on many occasions it turns out there is a need for storing the same key more then once, but with a different data part. E.g. this happens when you use a btree as an index on a database and in the field you are indexing a certain value appears more then once. In that case, you want to store the key value several times but with a different data part, namely the record number in the database. By default a key value can appear only once in the btree. If you try to insert a second entry with the same key value, it will simply replace the existing one. The option 'multiple_keys()' can be set to alter all that. Syntax: void multiple_keys(int YesNo); void multiple_keys_YES(void); void multiple_keys_NO(void); When the function 'multiple_keys()' is called with the argument set to TRUE, the btree will store a key value more then once. It is important to realize that the btree only keeps the key values sorted, NOT the data values. This means that when you are searching for a particular key/data value, the btree is capable of quickly locating the required key but has to find the correct data value by sequentially traversing all the inserted data belonging to that particular key. Btrees are intended to give quick access to key values by keeping them sorted, this does not apply to data values. Expecting anything else is misusing the btree. If you want quick access to a large number of different data values, all belonging to the same key, you need a different approach. The best thing to do is to construct a new btree and use a key which is a concatenation of the original key and the original data part. Setting the multiple_keys option has to take place before the 'define'. // Example #include "csbtree.h" void main(void) { typedef struct { char name[20]; int age; } record; BTREE bt; bt.multiple_keys_YES(); // Must be called before 'define' bt.define("btree.dat",sizeof(record),sizeof(long)); // By now a btree 'btree.dat' is created in the current working // directory with the multiple-keys option switched on. } 11.4 Current Pointer Contrary to TBASE, the btree class does use a 'current' pointer. When using btrees, the need to obtain the next (or previous) entry arises so often it's inevitable. The btree class spends as little time as possible on maintaining this current pointer. Therefore you should assume it is NOT set, unless you have strong reasons to believe otherwise. A limited set of functions can be used to set the current pointer. After that, the 'next()' and the 'previous()' functions can be used to move to the next resp. the previous entry. When these functions 'fail', which can be noticed from their return value, you should assume the current pointer is not set (any more). The next functions can be used to set the current pointer: - all the search functions. E.g. search_gt(), find() etc. - all the min() and max() functions. E.g. max_key(), min() etc. - the insert() function. The current pointer can be moved back and forth with the next() and previous() functions. Once the current pointer is set, the 'current()' functions can be used to obtain the key value and/or the data part. Any other function can, and probably will, render the value of the current pointer undefined! // Example // The next example displays the contents of a btree with // 'strings' as key fields. // It assumes that the btree 'demo.dbf' exists and that the // key fields are less then 100 bytes. #include "iostream.h" #include "csbtree.h" void main(void) { char buffer[100]; BTREEa bt; bt.open("demo.dbf",250); // Does not set the current pointer. // Make the first entry the 'current'. if(bt.min()) // This returns FALSE only if the btree is empty. do { bt.current_key(buffer); // Read the 'current' key value. cout< ge: Greater Equal >= lt: Less Then < le: Less Equal <= The functions return TRUE if such a key could be found, FALSE if not. Example: Assume the next table represents a btree. Entry Key value Data value 1 Blue 123 2 Green 45 3 Red 678 search_gt("Blue",Key,Data) Return value: TRUE Key: Green Data: 45 search_ge("Blue",Key,Data) Return value: TRUE Key: Blue Data: 123 search_lt("Blue",Key,Data) Return value: FALSE Key: undefined Data: undefined search_ge("Orange",Key,Data) Return value: TRUE Key: Red Data: 678 int search_dat_..(void *key,void *Data); int search_key_..(void *key,void *Key); The previous described functions return both the key value and the data value. In some cases this will be a waste of memory, therefore there are two similar sets of functions, which either return the found key value or the data part. search_dat_..() returns only the found data value. search_key_..() returns only the found key value. Example: With the same btree as in the previous example search_dat_gt("Blue",Data) Return value: TRUE Data: 45 search_key_ge("Blue",Key) Return value: TRUE Key: Blue int skip(int n); int skip(int n,void *Key,void *Data); int skip_key(int n,void *Key); int skip_dat(int n,void *Dat); A set of functions to move the current pointer. These functions are front ends for the next() and prev() functions. E.g. this is how the skip_key() function is implemented: int skip_key(int n,void *Key) { if(n>0) return next_key( n,Key); else return -prev_key(-n,Key); } So, with these functions the argument 'n' may be positive or negative. With 'n' positive the current pointer is moved to the 'end' of the btree. This is done by calling next(n). With 'n' negative the current pointer is moved to the 'beginning' of the btree. This is done by calling -prev(-n). The return value can be either positive or negative depending on the value of 'n'. When 0 is returned, the current pointer has not been moved or was not set. See also the prev() functions for more information. void zap(void); Closes the btree (when needed) and re-establishes all defaults. In other words: the zap() function restores the status of the btree immediatly after the class constructor. Its purpose is to open (or create) a new btree, starting of with all the parameters set to their default values. 12 CSDBGEN 12.1 Introduction CSDBGEN is a program generator. So far we have discussed the TBASE class which is capable of reading and writing records and the BTREE class which can be used as an index. CSDBGEN is needed to make a nice database out of this, which is capable of manipulating fields, maintaining indexes and the alike. It takes as input a 'definition file' which describes the fields and the indexes. From this it produces the source for a new C++ class. Member functions of this newly created class are used to access the fields. CSDBGEN does not generate a user-interface. There are several good reasons for using a program generator. - The TBASE class can concentrate on manipulating records rather then fields. Because of that, it remains a universal and efficient way to do disk IO. - With this approach it is easier to deal with field types that are not supported by the C programming language, particularly dates. - It is relatively easy for the program generator to convert to dBASE format because it has all the required knowledge at hand. Figuring out this conversion during runtime is a lot more complicated and will also make your executable larger because the knowledge to do the conversion is in the application instead of in the program generator. - Or more in general: everything the program generator does, can be left out from the application, making the executable smaller. - Without a program generator, the differences between the field types have to be dealt with runtime, perhaps even with every call accessing a field. Doing so, will inevitable result in some sort of an interpreter. Interpreters have two major drawbacks. First they are amazingly slow, and second they contain functions to handle every possible case. This means code is linked in for every known field type, even if it's not used. 12.2 Overview Using CSDBGEN starts of with creating a definition file. This file describes the layout of a record, the indexes, the name of the new class and the name of the files. When this file is created, CSDBGEN is called with this filename as a parameter. In return you will obtain the source for a brand new C++ class. This source is ready to compile, without the need for manual editing. This new class has public member functions for things like reading a field, setting the active index, exporting to ASCII, exporting to dBASE, reindexing, packing and so on. These member functions are very easy to use because they take very few, and often none, parameters. This is possible because a lot of the information which is specific for the database is already in the source of the functions. The generated class controls one database and all the indexes that come with it. If you need more then one database, as is to be expected, you have to repeat this procedure for the other databases as well. CSDBGEN does not aid in building the user-interface. An elaborate example is at the end of this chapter. 12.3 Features - Indexes with more then one reference to a record! This is an innovation! It makes it possible to locate a record by searching for a substring in the field, rather then the entire field. This topic is discussed in more detail in the paragraph 'tokenizing'. - Conversion to-and-from ASCII. This is convenient for backups, conversions to other systems and also for making changes in the record layout. - Export to dBASE compatible file format. CSDBGEN generates a function capable of writing out the contents of the database to a file which can be read by dBASE. - Can manage very large databases. The design of the libraries has been keen on avoiding limitations. By result, databases up to 2 billion records are theoretically feasible. - No overhead. Due to the use of a program generator, the overhead involved in accessing fields is next to none. - Large buffers. This system is capable of effectively using large amounts of RAM for buffering. - Fast. The precious two points, together with the efficient BTREEs guarantee a very fast database. 12.4 Limitations - No record locking. - No multi-user support. This system was designed to be used in single user applications. Time being, there is no support for network/shared databases. Perhaps there will be in the future but if so, it will take the form of a new series of classes. 12.5 Definition file The information needed to generate the new class is obtained from a 'definition file'. To get you started, the CSDBGEN utility is capable of generating an example. //example c:\borlandc>csdbgen /example>example.def Something like this will generate an example definition file 'example.def'. To get acquainted, let's look what's in it. Example definition file: class: NAM record: NAM_record file: demo field: name s 30 Y field: city s 20 field: birthday d Y field: salary f Explanation: line 1: class: NAM The program generator generates a class, which of course has to have a name. In this example 'NAM' . line 2: record: NAM_record As explained before, the database system uses a C structure as a record. The name of this structure is defined in the second line. In the generated header file the following (among others) will appear: #define NAME_LENGTH 30 #define CITY_LENGTH 20 typedef struct { char _name[NAME_LENGTH+1]; char _city[CITY_LENGTH+1]; long __birthday; float _salary; } NAM_record; line 3: file: demo This line indicates the name of the files the database system is going to use. In this example three files will be used: - demo.dbf, the TBASE database file. - demo01.idx, the BTREE index file on field 'name'. - demo02.idx, the BTREE index file on field 'birthday'. line 4/7: Field definitions. The syntax for a field definition is: field: [length] [format] [index] With: field_name, the name of the field. field_type, the type of the field which can be: i: integer l: long f: float F: double c: character s: string length, only for strings. Indicates the number of characters the field needs to be able to store. One additional byte is reserved to store the null terminator. format, only for date fields. Please, see the documentation on DATE fields. To give a quick example: MDY4 means, Month, Day and 4 positions for the Year. index, 'Y' means a normal index. 'T' means a 'tokenized' index. Nothing means no index at all. See also the documentation on 'tokenizing' further on. In the example: field: name s 30 Y A field 'name' of type string. 31 Bytes are reserved, 30 for the characters and 1 for the null terminator. An index is maintained for this field because of the additional 'Y'. field: city s 20 A field 'city' of type string with a length of 20 characters. There is NO index on this field. field: birthday d Y A field 'birthday' of type DATE. An index is placed on this field. field: salary f A field 'salary' of type float, without an index. 12.6 Tokenizing This is a new concept! Let's look at an example to make things clear. The demonstration database CSADDR, which comes with this package, uses tokenizing on its 'name' field. Let's say you have entered a record for the 'World Health Organization'. And now, for the first time in your life, can locate this record by entering 'health'. In fact, because CSADDR uses something called incremental search, entering just 'hea' is probably sufficient to pop up the record. It's important to know that this feature is not implemented by traversing the entire database from the first record to the last, as is done by some toy-applications. 12.6.1 How does it work? Traditionally, the index stores the entire field. In this example that would mean 'World Health Organization' is stored in the index together with a reference to the record number in the main database. With tokenizing, the index will store 3 entries, namely 'World', 'Health' and 'Organization'. This approach means that there will be a lot more entries in the btree then there are records in TBASE. In other words, an index is maintained on every suitable substring, rather then the entire field. To save disk space, the length of the key field in the BTREE is only half of the field length in the main database. 12.7 When is a substring indexed? First of all: tokenizing only applies for string fields. E.g. 'Tokenizing' a float field seams pointless. Whether or not a substring is put in the index is controlled by two things: - the way it is separated from the rest of the field. - the length of the substring. CSDBGEN generates a function 'tokenize'. This function contains a string 'delim' and a constant 'min_len'. // A part of the 'tokenize' function. char delim[]="\t,() "; // Token delimiters const min_len=4; // Minimum length for a token to be indexed The tokenize function separates the field into substrings according to the characters in the 'delim' array. Notice that the '.' is not a delimiter. This is to prevent abbreviations from being split up. A substring has to be at least four bytes long to appear in an index. This is not too long for most cases but it means that three letter abbreviations like 'IRS' are not indexed. Of course you can alter these two variables when needed. Example definition file: class: NAM record: NAM_record file: demo field: name s 30 T field: city s 20 field: birthday d field: salary f Notice the 'T' behind the 'name' field. This is short for 'tokenize'. The generated class will maintain an index for the name field with references for every suitable substring. 12.8 Export to dBASE The program generator also produces a member function: int to_DBASE(char *filename); When called, this function produces a file 'filename' which can be read by dBASE. Index files are NOT written. At this moment there is no corresponding function to do the conversion the other way round. 12.9 Exporting/Importing to/from ASCII int export(char *filename); This writes out the contents of the database to an ASCII file 'filename'. That file will also contain information about the fields. In this way the import() function knows how to process this data, even after changes in the record layout. int import(char *filename); This member function reads the ASCII file 'filename' and appends its data to the current database. It is meant to be used in conjunction with the export() function. The export function starts of with writing the entire definition file. The import function uses this information to skip fields which are not in the database and to read fields in the right order. Only fields which are also in the current database are read, the others are ignored. This mechanism can be used to make changes in the record layout. 12.10 Starting a new database A member function void define(void); is available to create a new database. If the database already exists, it is overwritten! 12.11 Opening a database The member function void open(void); opens an existing database. To avoid errors, a blank record is inserted when an empty database is opened. Index files are automatically rebuilt if they don't exist. 12.12 Current Record At any moment there is always a record the 'current record'. The functions to read and write fields all work with this current record. - After opening, the first record becomes the current record. - The go_to(), skip(), top(), bottom() and the search() functions can be used to make another record 'current'. 12.13 Accessing fields CSDBGEN generates two member functions for each field. One to read the field, and a second to write the field. The names are the same but the arguments differ. Example: // A part of the definition file: class: NAM record: nam_record file: dbtest field: name s 40 field: number i // We have a 'name' field consisting of a string with 40 characters // and a 'number' field which is an integer. // Among others, the next member functions are generated by CSDBGEN: class NAM { public: // For reading char *name(void); int number(void); // For writing void name(char *s); void number(int i); }; The next example gives an impression of how the generated class could be used. Example: void main(void) { NAM db; // We now have a class 'NAM'. db.open(); // Open database. Assuming it already exists. // No file names have to be entered. // All the indexes are opened automatically. puts(db.name()); // Displays the name field of the first record. // After 'open' the first record is 'current'. db.name("Pjotr Idaho"); // Changes the contents of the 'name' field to // 'Pjotr Idaho'. Indexes are updated automatically. db.close(); // Close database. } 12.14 DATE fields Standard C doesn't support date variables. Therefore, this library has its own DATE class. The functions to read and write date-fields are using a string representation of a date. These strings can represent a date in several formats. CSDBGEN uses a default of DMY4. This means 2 positions for the Day, 2 positions for the Month and 4 positions for the Year. Example: "02/04/1994" // By default interpreted as: April the 2th 1994. When a two position representation of the year is wanted, use Y2 instead of Y4. Every order of M,D,Y2 or Y4 is acceptable. Example: If you want "02/04/94" to be interpreted as February the 4th 1994, use the format MDY2. The line in the database definition file has to be: field: birthday d MDY2 If you want the field to be indexed, add an additional 'Y': field: birthday d MDY2 Y For more information about the date formats, please see the documentation of the DATE class in the CSA-library. On disk, dates are stored as longs. 12.15 Changing the record layout. Even when the database is already in use, the need to make changes in the record layout may occur. With the next procedure this can be accomplished quite easily, without to the need to reenter any record manually. To put it in a nut shell: save your data to an ASCII file with the old export() function and reload with the new import() function. Or in more detail: a) Export with the 'old' export() function. This will produce an ASCII file which fully resembles the database. b) Make a new definition file or alter the old one. c) Generate a new Class with CSDBGEN. d) Compile & link. The last three steps are simply the procedure for creating a database using CSDBGEN. e) Use the new import() function to reload the data. Import the ASCII file created with step 'a'. The import() function is doing the actual conversion. It can do this because it has knowledge of both the old and the new definition file. The old one is on top of the ASCII file and knowledge about the new one is hard coded in the import() function by CSDBGEN. 12.16 Member functions in alphabetical order Next is a list of the public member functions as they appear in the generated class. With the sole exception of open() and define(), the database needs to be open for these functions to work properly. void append_blank(void); Appends an additional record to the database. The record is filled with binary zeros and becomes the current record. void bottom(void); The current record is set to the last record according to the active index. void close(void); Closes the open files. All buffers are flushed and all allocated memory is released. This function is called automatically by the class destructor if needed. long curr_rec(void); Returns the number of the current record. The first record is number 1. void define(void); Creates a new database. Files are generated for the database and all the indexes. If a file already exists, it's overwritten. void delet(void); Marks the current record for deletion. After the pack() function is called all the, in this way marked, records will be removed from the database. int export(char *filename); Writes the contents of the database to an ASCII file 'filename'. This file is meant to be read back by the import() function. The exported file contains a header which resembles the database 'definition file'. The function returns TRUE on succes, FALSE otherwise. void go_to(long rec_nr); The record 'rec_nr' becomes the current record. This is independent of the active index! The record is directly looked up in the main database, not through an index. Whether or not the record is marked for deletion makes no difference. int import(char *filename); Reads records from an ASCII file 'filename' generated by the export() function and appends these records to the database. TRUE is returned on success, FALSE otherwise. int is_delet(void); This function returns TRUE if the current record is marked for deletion, FALSE otherwise. long numrec(void); Returns the number of records currently in the database. The records marked for deletion are also counted. void open(void); Opens the database for use. The define() function has to be called, that is, the database file needs to exist. Index files are automatically generated if they are missing. A runtime error is produced if something fails. int order(void); Returns the number of the current active index. void order(int index_number); This function controls the use of indexes. The variable 'index_number' indicates which index has to become the active index. All the indexes however, are updated when a record is altered. In the header file a preprocessor constant is defined for each index. The name of this constant is generated by converting the field name to upper case and adding _INDEX. Example: An index on field: Street Preprocessor constant: STREET_INDEX .order(STREET_INDEX); will make the index on the street field the active index. .order(UNSORTED); makes all the indexes inactive. Changing the active index does not alter the current record. The preprocessor constant UNSORTED can be used to render all the indexes inactive. The database will be browsed in its 'natural' order. void pack(void); Removes all the records marked for deletion. No temporary files are used! void reindex(void); Rebuilds all the indexes of the database. void search(void *key); The active index is searched for value 'key'. The current record becomes the first record which matches the search value. The function accepts a pointer to the search argument. The reason is that the indexes are not (necessarily) of the same type. E.g. one index may search for integers while another searches for strings. In this way the same search function can be used, no matter the field type. When the search argument is not exactly matched, the current record becomes the record with a value 'close to' the required key. Normally this will be the next 'higher' value. This strategy proofs to work fine when searching for names etc.. int skip(int delta); Makes another record the current record. Examples: skip(1); // The next record becomes the current record. skip(-1); // The previous record becomes the current record. skip(0); // Nothing happens. skip(10); // The record 10 positions to the end becomes // the current record. If an attempt is made to go 'before' the first record, record number 1 becomes the current record. Similar, the last record becomes the current record if an attempt is made to pass beyond the last record. The order in which the records are traversed is controlled by the current active index. The function returns the number of positions actually moved. int to_DBASE(char *filename); Exports the database to a file 'filename', which can be read by dBASE. Index files (for dBASE) are NOT generated. void top(void); The current record is set to the first record according to the active index. void undelet(void); If the current record is marked for deletion, this function removes the marker. Otherwise nothing happens. 12.17 Warning The program generator is not 'fool proof'. This means that you should avoid using names which already are reserved C++ key words. E.g. if you try to define a field with the name 'delete' the resulting source will not compile. 12.18 Example Let's say we want to build a database with stores a person's name and his/hers birthday. Step 1 First we need to construct a definition file. Next is a working example. class: BIRTH record: BRecord file: bdays field: name s 30 T field: birthday d Y4MD Y Assumes the name of this definition file is 'birth.def'. Step 2 From the definition file we have to generate the source for the database. We do that by calling CSDBGEN. c:\borlandc\csutil\test> csdbgen birth.def This produces two output files: 'birth.cpp' and 'birth.h'. These names are derived from the name of the definition file. Not from the class name as one might expect from this example. Step 3 We are now ready to start compiling. Normally, creating the database will be an option in the main menu of the application, but because this is a demonstration we do things differently. #include "birth.h" void main(void) { BIRTH db; // Declare an instance of the new BIRTH class. db.define(); // Create the database and its indexes. } Compile this together with the 'birth.cpp' file and link it. When ran, it should create three files: - 'bdays.dbf' The TBASE main database file. - 'bdays01.idx' The BTREEa index on the field name. - 'bdays02.idx' The BTREEl index on the field birthday. Remember, dates are stored as longs. If you run CSDIR in the same directory it will show something like this: Directory C:\BORLANDC\CSUTIL\TEST\ Name Size Type Entries Created Updated -------------------------------------------------------------------------- BDAYS.DBF 174 TBASE 0 Nov 01 1994 Nov 01 1994 BDAYS01.IDX 174 BTREEa 0 Nov 01 1994 Nov 01 1994 BDAYS02.IDX 174 BTREEl 0 Nov 01 1994 Nov 01 1994 -------------------------------------------------------------------------- Total: 522 bytes in 3 files. Step 4 By now, we have created the database files and we have the class to work with it. In other words, we are ready to write an 'application'. #include "iostream.h" #include "birth.h" void main(void) { BIRTH db; // Declare an instance of the new BIRTH class. db.open(); // Open it. Because it's empty, a blank record // is automatically added and becomes the current. db.name("Luke Skywalker"); // Modify the name. db.birthday("2015/07/03"); // Modify the birthday. db.append_blank(); // Add a new record. Becomes the current. db.name("Al Bundy"); // Modify the name. db.birthday("1945/11/30"); // Modify the birthday. db.reindex(); // Reindexing. For demonstration purposes. // Shouldn't be necessary. db.order(BIRTHDAY_INDEX); // Make BIRTHDAY the active index. db.top(); // Go to the oldest person. do { cout<csdir Directory C:\BIN\ADRES\ Name Size Type Entries Created Updated -------------------------------------------------------------------------- CSADR.DBF 98382 TBASE 298 Sep 20 1994 Oct 31 1994 CSADR01.IDX 40960 BTREEa 403 Oct 29 1994 Oct 31 1994 CSADR02.IDX 10752 BTREEa 104 Oct 29 1994 Oct 31 1994 CSADR03.IDX 4608 BTREEl 28 Oct 29 1994 Oct 31 1994 CSADR04.IDX 5120 BTREEa 22 Oct 29 1994 Oct 31 1994 -------------------------------------------------------------------------- Total: 159822 bytes in 5 files. As can be seen from this example, CSDIR displays: - the name of the class involved. - the number of entries in the database. - in case of a btree, the number of different keys. If the same key is entered twice, it is counted as one entry. - date of creation. - date of last update. Example of the /a option. c:\bin\adres>csdir /a Directory C:\BIN\ADRES\ Name Size Type Entries Created Updated -------------------------------------------------------------------------- ADRES.EXE 137872 DOS Oct 29 1994 CSDEMIO.DEF 277 DOS Apr 17 1994 BACKUP.TXT 34478 DOS Oct 29 1994 CSADR.DBF 98382 TBASE 298 Sep 20 1994 Oct 31 1994 CSADR01.IDX 40960 BTREEa 403 Oct 29 1994 Oct 31 1994 CSADR02.IDX 10752 BTREEa 104 Oct 29 1994 Oct 31 1994 CSADR03.IDX 4608 BTREEl 28 Oct 29 1994 Oct 31 1994 CSADR04.IDX 5120 BTREEa 22 Oct 29 1994 Oct 31 1994 ERROR.ERR 12964 DOS Oct 27 1994 -------------------------------------------------------------------------- Database files: 159822 bytes in 5 files. Other files: 185591 bytes in 4 files. -------- + --- + Total: 345413 bytes in 9 files. Another example: c:\bin\adres>csdir cs*.* /a Directory C:\BIN\ADRES\ Name Size Type Entries Created Updated -------------------------------------------------------------------------- CSDEMIO.DEF 277 DOS Apr 17 1994 CSADR.DBF 98382 TBASE 298 Sep 20 1994 Oct 31 1994 CSADR01.IDX 40960 BTREEa 403 Oct 29 1994 Oct 31 1994 CSADR02.IDX 10752 BTREEa 104 Oct 29 1994 Oct 31 1994 CSADR03.IDX 4608 BTREEl 28 Oct 29 1994 Oct 31 1994 CSADR04.IDX 5120 BTREEa 22 Oct 29 1994 Oct 31 1994 -------------------------------------------------------------------------- Database files: 159822 bytes in 5 files. Other files: 277 bytes in 1 files. -------- + --- + Total: 160099 bytes in 6 files. 17 CSINFO CSINFO is an on-line DOS utility to display information about a particular database. It only recognizes the databases made with the CSDB-library. An example of its output: c:\adres>csinfo csadr01.idx Information about database: csadr01.idx. Type..................: BTREEa Version...............: 1.0.a Class compiled at.....: Sep 05 1994, 04:28:24 With..................: Borland C++ 3.1 NOTE: The above information refers to the version of the class used during the CREATION of the database file. Btree created at......: September 20 1994, 10:02:11,47 Btree last updated at.: September 26 1994, 23:25:19,96 Multiple keys allowed.: YES Number of keys........: 622 Number of blocks......: 111 Block size............: 511 bytes Key size..............: 41 bytes Data size.............: 4 bytes Data degree...........: 10 Index degree..........: 10 Number of levels......: 4 18 CSERROR Normally all the errors are read from a file. This is the file 'error.err'. It has to be in the current working directory or it cannot be found. The advantage in using a runtime error file is of course the smaller executable that results from leaving out all the possible error messages. The error file is not kept open all the time. For opening a file, some dynamic memory allocations have to be done. This can lead to problems when the error message that has to be displayed results from an 'out of memory' condition. (It needs memory to say 'there is no more memory'.) To overcome this and other problems, CSERROR can be used. It generates C source that makes the runtime error file redundant. Example: c:\borlandc>cserror error.err This will produce a file 'error.cpp' in the current directory. Compile this and link it in with the rest of your application, but before the libraries. In this way the 'error.obj' will replace the 'csmess_read()' function which is in the library. // Example of how the resulting 'error.cpp' file could look: // Many errors are left out. #include "csmess.h" char *_csa_error[]= { "Error 9370: TBASE: %s Can't write report file %s. Disk full?", "Error 9390: TBASE: %s Out of memory during pack().", "Fatal Error 9545: PAGE: %s Header_2_data(): can't perform fseek.", "Fatal Error 9550: PAGE: %s Write_header: can't perform fwrite.", "Fatal Error 9555: PAGE: %s Header_2_data(): can't perform fread.", "Fatal Error 9560: PAGE: %s Can't open file during definition.", "Error 9562: PAGE: %s Can't open report file %s.", "TheEnd" //THIS HAS TO BE THE LAST LINE!! }; ///////////////////////////////////////////////////////////////////// char *csmess_read(long error) { char tmp[25]; ltoa(error,tmp,10); char **p=_csa_error; for(;;) { if(strstr(*p,tmp)) return *p; if(!strcmp(*p,"TheEnd")) return NULL; p++; } Notice the 'TheEnd' line, which was not in the original 'error.err' file. Never remove that line! 19 CSADD 19.1 Source This is a demonstration database for storing addresses. It gives an adequate impression of what this library was designed for. No complex client/server approach but strait forward functions to read and write records. The user interface is written with the aid of the CSA-library. The source consists of two files: 'csadd.cpp' for the user-interface and 'csaddio.cpp' for the database. - 'csaddio.cpp' is generated by the CSDBGEN utility with the file 'csaddio.def' as input. - 'csadd.cpp' is 'hand coded'. The file is about 500 lines. 19.2 Openings screen This is how it is supposed to look: eXit Insert Delete Edit Sort order Output Utilities Setup Help=F1 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ Address DataBase ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ³ ³ º Updated: 09/29/94 º ³ ³ º º ³ ³ º Name: City: º ³ ³ º Roberta Grundburg Kopenhagen º ³ ³ º º ³ ³ º Address: Telephone: º ³ ³ º 23 Grondlsy 0978-234-56756 º ³ ³ º º ³ ³ º Zip code: Country: º ³ ³ º TY 2347 Denmark º ³ ³ º º ³ ³ º Birthday: Relation: º ³ ³ º 12/04/1977 45 º ³ ³ º º ³ ³ º Info: º ³ ³ º º ³ ³ ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ³ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³ Command: rob Record 243/243 ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ 19.3 Features This simple demonstration database has already some interesting features: - Incremental search. That is, it 'zero's in' on the name you are looking for with every key entered. Normally it finds the required record after only two or three characters. - Exporting to a dBASE compatible file. The files used by the application are not in the dBASE format, but they can be exported to a dBASE compatible file. - Indexing also on substrings. Contrary to the traditional database, this one is capable of locating a record by searching for a substring of the key field, rather then the entire field. Part Five Part Five discusses the classes and functions implemented in the CSA-library. They have nothing to do with databases so you can use or ignore them at will. However, two chapters may require some attention. Alloc-logging which deals with heap corruption and memory leaks. The HEAP class for efficiently allocating large numbers of small blocks. 20 CSTOOLS 20.1 Introduction A collection of odds & ends, merely intended to support the other classes, but if you see something to your liking, please feel free to use it. The function prototypes are in cstools.h. int add_path(char *filen,char *path); Adds path 'path' to filename 'filen'. Afterwards 'filen' contains the new name. It returns TRUE if successful, FALSE otherwise. void box(int row,int col,int h,int w,int border,int color); Draws a box on the screen. (Non graphical.) row: top row col: left column h: height w: width border: type of border BORDER_NONE, BORDER_SPACE BORDER_SINGLE, BORDER_DOUBLE color: text ATTRIBUTE of the border line. int csrand(long int); long csrand(long amount); Returns a VERY random number in the range 0..(amount-1), including both 0 and (amount-1). int cstmpname(char *name); Generates the name of a non-existing file in the 'temp' directory. It first searches for the environment variable 'TMP' and if not found for 'TEMP'. A filename is generated which does not already exist in this directory. The function has to be called with a parameter 'name' pointing to a buffer large enough to hold the complete drive, path and filename. If non of the evironment variables exist, a filename for the current directory is produced. It only generates a filename, no file is actually created. The function returns TRUE if a unique filename was found, FALSE otherwise. int disk(char *s); Sets the current drive and path as indicated by string 's'. If 's' is the empty string, 's' is set to the current drive and path! It returns TRUE if successful, FALSE otherwise. void empty_kb(void); Empties the keyboard buffer. int file_exist(char *fnaam); Returns TRUE if file 'fnaam' exists. char *file_ext(char *name,char *ext); Adds an extension to filename 'name'. It returns a pointer to an internal buffer which contains the new name. If 'name' already has an extension, it is overwritten. The string 'name' itself is not changed! long filesize(char *name); Returns the size of file 'name'. void filter_string(char *source,char *allowed); All the characters in 'source' which are not in 'allowed' are removed from 'source'. void gotoyx(int y,int x); Same as Borlands gotoxy(x,y) but with reversed parameters. int is_color(void); Returns TRUE if you are using a color screen. FALSE otherwise. void lower_upper(char *ptr); Converts the entire string to upper case. long lrandom(long amount); Returns a long random number in the range 0..(amount-1), including both 0 and (amount-1). int make_color(int fgc,int bgc); Returns the text attribute corresponding with the Fore Ground Color (fgc) and the Back Ground Color (bgc). size_t next_prime(size_t pri); Calculates next higher prime number. char *notabs(char *s); Replaces every occurrence of a tab character in 's' with a single space. That is: tabs are not expanded, but simply removed. char *remove_space(char *s); Removes ALL the blanks from the string 's'. It returns character pointer 's'. unsigned int sqrti(unsigned int n); unsigned long sqrtl(unsigned long n); Calculates the sqrt from n WITHOUT USING FLOATING POINT arithmetic. void str_split(char *source,char ch,char *first,char *last); Split 'string' source at the first occurrence of character 'ch'. Ch is included in neither the 'first' nor the 'last' string. void str_strip(char *source,char *remove); Characters in 'source' which are also in 'remove' are removed from 'source'. int str_equal(char *s1, char *s2); Returns TRUE if 's1' is equal to 's2', discriminating between upper and lower case. void str_left(char *source,char *dest,int len); Copies at most 'len' number of characters from the left of 'source' to 'dest'. char *string_replace_ones(char *source,char *d,char *r); Replaces the first occurrence of 'd' in 'source' with 'r'. int string_replace(char *s,char *d,char *r); Replaces every occurrence of 'd' in 'source' with 'r'. It returns an integer number indicating the number of times a substitution was made. long time_stamp(void); Returns a higher long number on each successive call, starting with zero again when MAXLONG is reached. void trim_string(char *s); Removes heading and trailing blanks from string 's'. void wait(long msec); Waits 'msec' milliseconds. void waitkb(long msec); Waits 'msec' milliseconds or until the next keyboard hit. 21 CSKEYS Almost all the input for the library functions is done through the cskey() function. Syntax: int cskey(void); The return value can be one of the following: ( defined in CSKEYS.H ) CTRL_A KEY_A KEY_a ALT_A DELETE CURSOR_UP CTRL_B KEY_B KEY_b ALT_B END CURSOR_DOWN CTRL_C KEY_C KEY_c ALT_C HOME CURSOR_RIGHT CTRL_D KEY_D KEY_d ALT_D PAGE_UP CURSOR_LEFT CTRL_E KEY_E KEY_e ALT_E PAGE_DOWN CTRL_F KEY_F KEY_f ALT_F INSERT CTRL_G KEY_G KEY_g ALT_G BACKSPACE CTRL_H KEY_H KEY_h ALT_H TAB CTRL_I KEY_I KEY_i ALT_I SHIFT_TAB CTRL_J KEY_J KEY_j ALT_J ENTER CTRL_K KEY_K KEY_k ALT_K ESC CTRL_L KEY_L KEY_l ALT_L SPACE CTRL_M KEY_M KEY_m ALT_M CTRL_N KEY_N KEY_n ALT_N CTRL_DELETE CTRL_O KEY_O KEY_o ALT_O CTRL_HOME CTRL_P KEY_P KEY_p ALT_P CTRL_CURSOR_UP CTRL_Q KEY_Q KEY_q ALT_Q CTRL_CURSOR_DOWN CTRL_R KEY_R KEY_r ALT_R CTRL_CURSOR_RIGHT CTRL_S KEY_S KEY_s ALT_S CTRL_CURSOR_LEFT CTRL_T KEY_T KEY_t ALT_T CTRL_PAGE_UP CTRL_U KEY_U KEY_u ALT_U CTRL_PAGE_DOWN CTRL_V KEY_V KEY_v ALT_V CTRL_END CTRL_W KEY_W KEY_w ALT_W CTRL_X KEY_X KEY_x ALT_X CTRL_Y KEY_Y KEY_y ALT_Y CTRL_Z KEY_Z KEY_z ALT_Z F1 SHIFT_F1 CTRL_F1 ALT_F1 KEY_1 ALT_DELETE F2 SHIFT_F2 CTRL_F2 ALT_F2 KEY_2 ALT_HOME F3 SHIFT_F3 CTRL_F3 ALT_F3 KEY_3 ALT_CURSOR_UP F4 SHIFT_F4 CTRL_F4 ALT_F4 KEY_4 ALT_CURSOR_DOWN F5 SHIFT_F5 CTRL_F5 ALT_F5 KEY_5 ALT_CURSOR_RIGHT F6 SHIFT_F6 CTRL_F6 ALT_F6 KEY_6 ALT_CURSOR_LEFT F7 SHIFT_F7 CTRL_F7 ALT_F7 KEY_7 ALT_PAGE_UP F8 SHIFT_F8 CTRL_F8 ALT_F8 KEY_8 ALT_PAGE_DOWN F9 SHIFT_F9 CTRL_F9 ALT_F9 KEY_9 ALT_END F10 SHIFT_F10 CTRL_F10 ALT_F10 KEY_0 F11 SHIFT_F11 CTRL_F11 ALT_F11 F12 SHIFT_F12 CTRL_F12 ALT_F12 The predefined values for the 'normal' keys like 'A', 'a' or '1' are the same as the ASCII values. This means you are not forced the type things like if( KEY_A==cskey() ) .... but can also use: if( 'A'==cskey() ) ...... 21.1 CSKEYS.exe There is also a simple utility to test the return value of the cskeys() function. This is cskeys. With this it becomes very easy to make your own additions to the predefined keys in 'cskeys.h'. 22 HEAP 22.1 Purpose To avoid having to allocate many small blocks, a special HEAP class is implemented. The idea is to do allocations in chunks of about 2Kb and take the small amounts from that when needed. This approach has considerable advantages. - You can release all allocated memory with just one function call instead of freeing many small blocks separately. - It is a lot faster because normal heap operations are relatively slow. - Heap efficiency is also improved. It is much easier for the heap to deal with relatively few allocations of about 2Kb then it is to deal with numerous small allocations. - It can save valuable memory. There is considerable overhead involved in using the heap. Apart from what you need, several additional bytes are used to 'pointer' the allocated blocks together. In addition, allocations are done in multiples of 16 bytes. This can lead to a situation where you need only 13 bytes while 32 bytes are used! 22.2 When to use it? The HEAP class is particularly useful when dealing with pointer structures in ram. Pointer structures are small, all of the same size and an application will probably use a lot of them. The BUFFER class described earlier in this documentation also uses the HEAP class. This means you can use it without enlarging your application. The HEAP class assumes allocations of a fixed size. This limits its usefulness but improves efficiency. It is very well possible to use more then one instance of the HEAP class in an application. It is feasible to use a different HEAP for every size of allocation needed. The class was designed with small allocations in mind, something below 50 bytes. It is doubtful whether the HEAP class still makes sense for allocations above 100 bytes. Summarizing: - Allocations have to be of a fixed size. - Allocations have to be small, below 50 bytes. - Many allocations of this type are going to take place. 22.3 Using HEAP. Using the HEAP class starts of with an initialization, stating the size of the allocations. Afterwards the class has to be 'opened'. From there on allocations can be made, and blocks can be free-ed again. When the work is done, the close or the zap function can be called to free all allocated memory. // Example #include "csheap.h" void main(void) { typedef struct { void *next; void *prev; int number; } pStruct; // A typical pointer structure. HEAP heap; // HEAP class instance. heap.init(sizeof(pStruct)); // Initialize it for the size of the // pointer structure. heap.open(); // Open the class so it can be used. pStruct *p,*q; p=(pStruct *)heap.malloc(); // Allocation. q=(pStruct *)heap.malloc(); // Allocation. p.next=q.prev=NULL; // Doing something. // Doing much more. heap.free(q); // Freeing q. heap.close(); // Finally finished. // Freeing all allocated memory. } 22.4 Functions in alphabetical order. The function prototypes are in CSHEAP.H. void close(void); Closes the class. All allocated memory is freed. The initialisation parameters are retained, which makes it possible to reopen the class without calling init(). This function is also called by the class destructor. void empty(void ); Frees all allocated memory, but the class remains open. void init(U16 alloc_size,U16 page_size=2048); Initializes the class. 'Alloc_size' is the size of the allocations needed. 'Page_size' is the size of the chunks which are going to be allocated from the heap. This parameter has a default value of 2048 bytes. int open(void); Opens the class. The 'init()' function has to be called first. The function returns TRUE on success and FALSE otherwise. void free(void *p); Frees the previously allocated block 'p' is pointing at. void *malloc(void); Allocates a block and returns a pointer to it. If no memory is available, a NULL pointer is returned. void zap(void); Frees all allocated memory and closes the class. The initialization parameters set by the init() function are reset. This means the class has to be initialized again before it can be reopened. 23 Alloc-Logging 23.1 Introduction Dynamic memory allocations can create problems which are difficult to trace. Therefore, this library contains a set of functions which can be used to replace the normal malloc() and free() functions. The replacements can be made to write a record to a log. This log can be used later to check for memory leaks. The replacements also test for things like freeing a NULL pointer or a malloc which returns NULL. In the DOS version, Borlands 'heapcheck' functions are called to test for heap integrity. Replacements are preprocessor commands which can be switched on and off with the preprocessor variable CS_DEBUG. If CS_DEBUG is not defined, the normal functions are called. 23.2 Replacements Replacements are available for the following functions: Prototypes in csmalloc.h. Function: Replacement: malloc csmalloc calloc cscalloc realloc csrealloc free csfree farmalloc csfarmalloc farcalloc csfarcalloc farrealloc csfarrealloc farfree csfarfree The use of the replacements is fully equivalent to the original. The allocations in the CS-libraries are always done by calling the replacements. The production version of the libraries was compiled without CS_DEBUG being defined, so the normal functions are used and are called without any additional overhead. When the debug version was compiled, CS_DEBUG was defined and as a result all the allocations done by the library functions can be logged! 23.3 Logging Logging of allocations can be switched on and off through the use of two functions. void alloc_logging(int TrueFalse); After a call to alloc_logging(TRUE) all the allocations are logged in the ASCII file 'malloc.log'. Calling alloc_logging(FALSE) switches the logging off. void alloc_logging(int TrueFalse,char *name); This is basically the same as the previous function but has the additional option of specifying the name of the log file. The next example displays a part of an allocation log. 4E79:0004 file csedst30.cpp line 8: malloc() 8 bytes 4E7A:0004 file csedst30.cpp line 8: malloc() 8 bytes 4E78:0004 file csedst30.cpp line 23: free() 4E78:0004 file csedst30.cpp line 8: malloc() 9 bytes 4E79:0004 file csedst30.cpp line 23: free() 4E7B:0004 file csedst30.cpp line 8: malloc() 22 bytes 4E7B:0004 file csedst28.cpp line 12: realloc free() 4E7B:0004 file csedst28.cpp line 12: realloc malloc() 4E7A:0004 file csedstr.cpp line 15: free() 4E7B:0004 file csedstr.cpp line 15: free() As can be seen, the first column displays the pointer involved, the second and third display the file and the line where the call was made. When an allocation is concerned its size is also displayed. Reallocs appear as two lines. 23.4 Memory Leaks. With a log like this it is easy to check for memory leaks. In fact, a command-line utility is supplied to check for that. It is called CSMALLOC. Only one parameter needs to be supplied: the name of the allocation log. Example c:\test>CSMALLOC malloc.log If it encounters a malloc which is not matched by a free, it displays the pointer involved. Like: UNMATCHED address: 29CC:0004 If all malloc's are matched by a free it says something like this: NO ERRORS encountered!! Number of addresses: 23 Lowest address: 29CC:0004 Highest address: 2EAE:0004 If malloc logging is kept on for longer periods, the log file can become extremely large. However, this poses no problem for CSMALLOC. If you are planning to use this method to detect memory leaks, it is essential to switch on the logging before the first allocation is done. Don't forget class constructors do allocations as well! 24 CSEDSTR The class for manipulating strings. Instead of providing the formal syntax we will clarify things by supplying a large number of examples. Class name: EDSTR Prototypes are in CSEDSTR.H. #include "csedstr.h" main(void) { EDSTR str; str=" A test "; // Assign a string str.upper(); // Convert to upper case str.lower(); // Convert to lower case str.trim(); // Remove all leading and trailing blanks // str contains now: "a test"; str+=" At the end "; // APPEND at the end // str contains now: "a test At the end" int i=-345; str=i; // Convert the integer value to string // str contains now: "-345"; str="1001"; i=str; // Assign string to integer str="A line"; str.strip("ijkl"); // Stripping the characters i,j,k,l from // str. // Str now contains: "A ne"; str="A line"; str.filter("ijkl"); // Allow only the characters i,j,k,l in // str. // Str now contains: "li"; str="The quick brown fox"; str[4]='Q'; // Str now contains: "The Quick brown fox" EDSTR str2="by C++ !"; str="Made possible "; str=str+str2; // Str: "Made possible by C++ !"; if( strstr2) .. if( str<=str2) .. if( str>=str2) .. if( str==str2) .. // Comparisons are possible. // Case is always ignored. gotoxy(5,20); clreol(); str.edit(15,25); // User can edit the string. // Only 15 characters are displayed. // The maximal length of the string // is 25 characters. // The string will scroll if it // becomes longer then 15 characters. // 'Enter' or 'Escape' will // terminate the function. } 25 CSWINDOWS 25.1 Introduction The WINDOW class is an implementation of the well known character-based text windows. The window toolbox can be used in two distinct ways: 1) through the use of 'old-fashioned' C functions. 2) as a C++ class. The member functions of the WINDOW class are fine for manipulating one instance of the class, but the C-functions seem to have the advantage when dealing with more then one instance. E.g. shifting from one window to another or removing all windows at once involves more then one instance of the WINDOW class, so this is done by C-functions rather then class member functions. The examples above already gives you an impression of which functions to use when. When you are dealing with ONE window use the class, in all other cases use the global C functions. For the sake of completeness, the syntax of all the C-style functions is given, but whenever possible use the class member functions. For those who are becoming nervous: - You normally will need only two very simple C functions. - A few examples will make things clear! 25.2 General Information Class name: WINDOW Prototypes are in CSWINDOW.H. - Every window is uniquely identified by a positive integer number. - In practice the number of windows an application can use is only limited by the available amount of memory. - Windows can 'overlay' each other. When an underlying window is called to be brought to the top, some flickering may appear because the still overlying windows must first be removed, and later be replaced. - Checks are made to see if the window which has to be brought to the top is overlaid. If not, it will become immediately active. - The windows are 'chained' together. The windows which are used last are closest to the end of the chain. When you are worried about performance: a) try not to activate a window which is overlaid, b) use only windows close to the end of the chain. However, under normal circumstances you will never have to worry about performance, because the window functions are very fast. 25.3 The C++ version, the class WINDOW // Example: #include "dos.h" #include "cswindow.h" main(void) { WINDOW demo; // Declare a window demo.height(10); // The number of lines demo.width(40); // The number of columns demo.head(" A Demo Window ");// A text in the border demo.shadow(SHADOW); // Make a shadow demo.activate(); // Display the window gotoxy(5,5); // Write the famous cprintf(" Hello World !! "); // words in the window. waitkb(5000); // Wait 5 seconds. } 25.3.1 Syntax of the MEMBER functions void top(int) Sets the top row of the window. If you enter -1 the window is centered vertically on the screen. Default is -1. void left(int) Sets the left column if the window. If you enter -1 the window is centered horizontally on the screen. Default is -1. void height(int) Sets the height of the window. Default is 24. void width(int) Sets the width of the window. Default is 80. void set_dim(int top,int left,int height,int width) This set all four at once with this function. void border(int) Sets the border type. Four types are pre-defined in the header file CSWINDOW.H. 1: W_BORDER_NONE No border. 2: W_BORDER_SINGLE Border is a single line. 3: W_BORDER_DOUBLE Border is a double line. 4: W_BORDER_SPACE Border made out of 'space' is drawn around the window. The default is BORDER_SINGLE. void head(char *) Sets the heading text of the window. This text is at center of the top of the window, but only if a border is The maximum length of this string is 49 characters. The default is the empty string. void shadow(int) A value of 0 means no shadow, any other value will create a shadow. In CSWINDOW.H are two values pre-defined. 1: NO_SHADOW Don't display shadow. 2: SHADOW Display shadow. The default is NO_SHADOW. void activate(void) This makes the window appear on the screen, on top of all other windows. The first time it will also create the window. If the window already exists, but is lying beneath other windows, it is shifted to the top. void border_color(int) void screen_color(int) Sets the text-attribute of the border and the text screen. Notice: the text ATTRIBUTE. This means both the foreground and the background colors are set. This is implemented through a call to Borland's TEXTATTR() function. See their documentation for more information. You can also use the 'make_color()' function defined in 'cstools'. int border_color(void) Returns the text-attributes of the border. int screen_color(void) Returns the text-attributes of the screen. void clear(void) Clears the text screen. This is done automatically when the window is first created. void remove(void) Remove the window permanently. The previous active window will become the active window again. void remove_all(void) Removes ALL windows. int adjust(void) Even if the window already exists and is visible on the screen, you can make changes. Just call the appropriate functions ( e.g. height() to change the height etc.) and when you are done call 'adjust()'. The window will then change accordingly. The function returns TRUE if successful FALSE otherwise. int previous(void) Returns to the previous active window. That is: the window that was active before the current window became active. The function returns TRUE if successful FALSE otherwise. int user_adjust(void) This function gives the application user control over the window. He/she can change the position of the window with the cursor keys and the size through use of the control-cursor keys. The function ends when ENTER is pressed. The function returns TRUE if successful FALSE otherwise. void auto_delete(int) Sets the auto delete option. If called with a value unequal to 0, the window will be removed if the class instance is destructed. Otherwise the window will remain on the screen even if the class instance no longer exists. The default is ON. void scroll(int ROW,int COL) Make the text in the window scroll. ROW: scrolling up/down. A positive value means up (e.g. line 2 will become line 1). COL: scrolling left/right. A positive value means to the right (e.g. column 1 will become column 2). void get_dim(int *row,int *col,int *h,int *w) Returns the size-related-parameters of the window. The values returned do include the border but NOT the shadow. *row: Top row. *col: Left column *h: height *w: width These values need NOT be the same as the ones you assigned, because the window is adjusted to the limitations of the screen. (e.g. if you set height to 50, this function will tell you it is 25. ) void get_in_dim(int *row,int *col,int *h,int *w) The same as the previous function but now the returned values do NOT include the border and do NOT include any shadow. So, the inner dimensions are returned. void work_size(int *height,int *width) Same as the previous function, but only the height and the width are returned. int number(void) Returns the number of the window. Only needed if you are using the C functions as well. 25.4 The C version As stated before, every window is identified by a unique integer number. In the C++ version you don't have to worry about this number. In the C version however, it is of prime importance. If a function has parameters, the first parameter is always the number of the window you are referring to. As pointed out in the introduction you are supposed to use the class functions whenever possible. In fact there are only two functions of the C version you should use. These are: int win_current(void); Returns the number of the current active window. You might need this number if you want to return to this window somewhere later on in your application. int win_shift(int num); Make window 'num' the active window. Useful in combination with the previous function. In rare cases: void win_remove_all(void); Remove all windows. int win_default(void); For writing outside any existing window. 25.4.1 Example Problem: You want to write an error handling function which writes a message at the bottom line of the screen and, after some waiting, returns to the original window and continues. The problem is, the function can be called from 'anywhere' and at the moment you are writing the function, it is not clear to which window to return. #include <......> WINDOW w_error; // Window for the messages, global... void disp_error(char *s) { int old_window=win_current(); // The window the function // is called from. w_error.activate(); // Make the error window // the active window. cprintf("\n\r %s ",s); // Print the text. wait(2500); // Wait 2.5 seconds. win_shift(old_window); // Return to the original window! } main(void) { w_error.set_dim(23,1,3,80); w_error.head(" E r r o r s "); w_error.activate(); // The window permanent visible // A lot of other windows // The program calls. } Note: In this simple example the 'previous' function also would have solved the problem. In more complicated cases however, this method can very well be the only solution. 25.5 Syntax of the C functions int win_make( int& num, int border_color, int screen_color, char *head, int top, int left, int height, int width, int border, int shadow); Parameters: num The number of the new window. If a window with this number already exists it is deleted. In the header file CSWINDOW.H is predefined W_NEW. If you call the function with 'num' equal to W_NEW a new window is created and num is changed into a number which was formerly not used. To make this possible num is passed by reference. border_color screen_color Sets the text-attribute of the border and the text screen. Notice: the text ATTRIBUTE. This means both the foreground and the background colors are set. This is implemented through a call to Borland's TEXTATTR() function. See their documentation for more information. head Sets the heading text of the window. This text is displayed at the center of the top of the window, but only if a border is present. top Sets the top row of the window. If you enter -1 the window is centered vertically on the screen. left Sets the left column if the window. If you enter -1 the window is centered horizontally on the screen. height Sets the height of the window. width Sets the width of the window. border Sets the border type. Four types are predefined in the header file CSWINDOW.H. 1: W_BORDER_NONE No border. 2: W_BORDER_SINGLE Border is a single line. 3: W_BORDER_DOUBLE Border is a double line. 4: W_BORDER_SPACE Border made out of 'space' is drawn around the window. void win_remove(void) Remove the active window from the screen. The previously active window will become the active window again. void win_remove_all(void) Removes ALL windows. int win_shift(int num) Will shift window with number 'num' to the top. The function returns TRUE if successful FALSE otherwise. int win_present(int num); Returns TRUE if window 'num' exists, FALSE otherwise. int win_first_unused(void) Returns the lowest positive number which is not 'in use' by a window. int win_current(void) Returns the number of the current active window. If no window is active it returns -1. void win_clear(void) Clears the current active window. int win_adjust( int num, int border_color, int screen_color, char *head, int top, int left, int height, int width, int border, int shadow); Is used to make changes to an existing window. The parameters are the same as with win_make(). Call this function with the parameters set to the newly desired values and your window will change accordingly. The function returns TRUE if successful FALSE otherwise. int win_previous(void) Returns to the previous active window. That is: the window that was active before the current window became active. The function returns TRUE if successful FALSE otherwise. int win_user_adjust(void) This function gives the application user control over the current active window. He/she can change the position by the cursor keys and the size by the control-cursor keys. The function ends when ENTER is pressed. The function returns TRUE if successful FALSE otherwise. void win_scroll(int num, int ROW,int COL) Make the text in the window 'num' scroll. ROW: scrolling up/down. A positive value means up (e.g. line 2 will become line 1). COL: scrolling left/right. A positive value means to the right (e.g. column 1 will become column 2). void win_get_dim(int num,int *row,int *col,int *h,int *w) Returns the size-related-parameters of window 'num'. The values returned do include the border but NOT the shadow. *row: Top row. *col: Left column *h: height *w: width These values need NOT be the same as the ones you assigned, because the window is adjusted to the limitations of the screen. (e.g. if you set height to 50, this function will tell you it is just 25. ) void win_get_in_dim(int num,int *row,int *col,int *h,int *w) The same as the previous function but now the returned values do NOT include the border and do NOT include any shadow. So, the inner dimensions are returned. win_work_size(int num,int *height,int *width) Same as the previous function, but only the height and the width are returned. int win_default(void) Not a window at all. Intended for writing outside any existing window. The x,y coordinates are set back to the full screen, as it is when you are working without windows. The software represents this virtual window by number -1. This number can also be returned by the win_current() function. 25.6 Working within a Window Working within a window is the same for both versions. All the above mentioned functions are built around the compiler-supplied function window(). This means you can use all Borlands I/O functions that are intended to work relative to a window. The most important ones are: gotoxy(); // To move the cursor putch(); // Displaying a character in the window cputs(); // Displaying a string delline(); // Delete a line insline(); // Insert a line clrscr(); // Clears the window clreol(); // Clears until end of line getche(); // Read a character cgets(); // Read a string cprintf(); // Window version of printf() // Don't forget, you need "\n\r " // to go to the start of the next line. // cprintf("\n "); will put the cursor // one line down, without going to the // start of that line. 25.7 File Browsing The WINDOW class has a member funtion 'browse', which aid in displaying help-files. // Example WINDOW help; // Create Window for help text. help.set_dim(-1,-1,18,70); // Set size and position. help.head(" Help Screen "); // A Header. help.color(HlpBorCol,HlpScrCol); // Set the colors. help.browse("help.txt"); // Browse the help file. The help file has to be an ASCII file. Supported keys: ESC and ENTER: Exit. Cursor Up: 1 line up Cursor Down: 1 line down Page Up: 1 screen up. Page Down: 1 screen down. Home: Top of file. End: End of file. 26 CSMENU 26.1 General Information There are a lot of ways to implement a menu system. One very common method concentrates on directly calling functions from within the menu. This is not the method used here, because it seems to limit its usefulness. This menu system returns an integer value when an option is chosen. The idea is to make the return value equal to a certain function key connected with the option. The application user can then browse through the menus and select an option or hit the function key directly. In both cases your program has to deal with the same integer value which can easily be processed by applying a 'switch' statement. Examples will explain things further. There is no difference between a menu and a sub-menu. The definition is the same in both cases. The difference lies in the application of a 'connect' function which makes one menu the sub-menu of another. There are no limitations to the amount of menus you can use. The menus can be connected to arbitrary depth. The menus 'remember' which option was selected the last time the user was browsing through it. This option is again highlighted when the menu is entered. Function protypes are in csmenu.h. 26.2 Defining a menu int type(int hor_or_ver) Sets the type of the menu. There are two types: horizontal or vertical. In CSMENU.H two constants are predefined: MENU_HOR for a horizontal menu and MENU_VER for a vertical menu. The default is MENU_VER. void hold(int HoldRelease) A menu can be permanent on the screen or can be automatically removed after being used. This function allows you to control this. In CSMENU.H two constants are predefined: MENU_HOLD makes the menu remain on the screen and MENU_RELEASE which removes the menu afterwards. The default is MENU_RELEASE. void relative_pos(int ScreenCursor) The position of the menu can be set relative to the screen or relative to the cursor position at the moment of creation. ( Relative to the cursor is very convenient for submenus. ) In CSMENU.H two constants are predefined: TO_SCREEN for positions relative to the screen and TO_CURSOR for positions relative to the cursor. The default is TO_SCREEN. void top(int w) void left(int w) Sets the left column and the top row of the left-top corner of the menu. The coordinates are relative to the screen or the cursor, depending on the application of the relative_pos() function. If relative_pos is set to TO_SCREEN a value of -1 will center the menu. void width(int) void height(int) Sets the height and the width of the menu. If you don't use them or use zero, the appropriate values are calculated automatically. Advise: don't use them. int add_option(char *option,int key) Adds an option to the menu. The order in which they appear in the menu is the same as the order in which they are defined. The string 'option' defines the option-string. You can use a '~' to assign a key to the option. The return value is the value 'key'. Example: MENU m1; m1.add_option(" Se~tup ",123); // Defines option ' Setup ' as the first option // of menu m1. This option can also (apart // from using the cursor keys) being // selected by pressing 't' or 'T'. // If chosen, the menu system will return // the value 123. void enable_option(int num,int YesNo) With this function you control whether-or-not an option can be selected. Parameters: num Option number 'num'. The first option is number ONE. YesNo YesNo=TRUE means option is selectable. YesNo=FALSE means option can NOT being selected. By default all options are enabled. void enable_option(int YesNo) The same as the previous function, but applied to the last added option. void border_color(int) void screen_color(int) void option_color(int ) void key_color(int ) Sets the screen ATTRIBUTE of respectively the -border of the menu, -the 'normal' text in the menu, the not selected options. -the option which is currently selected -the hot key connected with the option Acceptable defaults are provided, depending on the type of screen. void color(int border, int screen, int option, int key) Sets all four colors at once. 26.3 Connecting menus The previous described functions are all concerned with defining just one menu. Of course you want to use a more complex menu structure which includes several sub-menus. The method to accomplish this is a simple one. Just define all submenus with the previously mentioned functions and then 'connect' them by calls to the connect(int, MENU &) function. int connect(int num, MENU &m2) Makes menu 'm2' a sub menu of this menu, 'dangling' under option 'num'. The first option is number ONE. Example: #include "cswindow.h" #include "cskeys.h" #include "csmenu.h" MENU m1,m2; m1.add_option(" File ", ALT_F); m1.add_option(" Setup ",ALT_S); m1.type(MENU_HOR); m1.left(1); m1.top(1); m1.border(BORDER_NONE); m2.add_option(" ~Load ", F3); m2.add_option(" ~Save ", F2); m2.left(1); m2.top(1); m2.relative_pos(TO_CURSOR); m1.connect(1,m2); 26.4 Displaying the menu void standby(void) If the menu is defined, it can be put on the screen by calling the standby() function. This will make all the (sub-) menus which are defined with MENU_HOLD become visible. 26.5 Using menus There are two ways to use the menu system: 1) The straightforward method. This is by a call to: int rc=choose(int &option). The user can select an option by using the cursor keys, escape, enter etc.. Afterwards rc indicates the way the menu is left. If rc==ESC the menu is left by pressing the escape key, 'option' is undefined. If rc==ENTER the menu is left by pressing 'enter', and 'option' contains the return value corresponding with the selected option. 2) The hard way. In some, more complicated, cases the simple choose() function will not do, as is made clear by the following problem. Problem: Let's say you have a program in which the user can load a file by pressing F3. He also can reach the same option by browsing through the menus. Typically you want the load option to remain highlighted on the screen while your user is typing in the file name. How to accomplish this? Solution: The int show(int key) function is intended to solve this problem. Show(key) searches through the menus looking for the value 'key'. It returns TRUE if 'key' is found, FALSE otherwise. Assuming the required value is found, all the involved (sub-)menus are automatically displayed. Afterwards things can be brought back to normal by a call to standby(). Example: #include "cswindow.h" #include "cskeys.h" #include "csmenu.h" MENU m1,m2; m1.add_option(" File ", ALT_F); m1.add_option(" Setup ",ALT_S); m1.type(MENU_HOR); m1.left(1); m1.top(1); m1.border(BORDER_NONE); m2.add_option(" ~Load ", F3); m2.add_option(" ~Save ", F2); m2.add_option(" e~Xit ", ALT_X); m2.left(1); m2.top(1); m2.relative_pos(TO_CURSOR); m1.connect(1,m2); m1.standby(); // Finally display something. int key; do { key=cskey(); // Read a key // ESC means browsing through the menus. if(key==ESC) m1.choose(key); // key now contains the option, either // by directly typing it in or selected // from the menu. switch(key) { case F3: // Loading a file. m1.show(F3); // Both the menus are now being // displayed. // The option 'load' is highlighted. // // You can now call the load function. load_function(); // Not defined. // m1.standby(); // Bring the menu system back in its // default status. break; case F2: // Saving a file. // some // code to // save. break; } } while (key!=ALT_X); // Use ALT_X to terminate loop. Syntax: int choose(int &option) Use: int rc=choose(option); The user can select an option by using the cursor keys, escape, enter etc.. Afterwards rc indicates the way the menu is left. If rc==ESC the menu is left by pressing the escape key and 'option' is undefined. If rc==ENTER the menu is left by pressing 'enter', and 'option' contains the return value corresponding with the selected option. int choose_hold(int &option) Same as choose() but the selected option and the selected (sub)-menu(s) remain visible after the function has returned. int show(int key) Show(key) searches through the menus looking for the value 'key'. It returns TRUE if 'key' is found, FALSE otherwise. Assuming the required value is found, all the involved (sub-)menus are automatically displayed. Afterwards things can be brought back to normal by a call to standby(). 26.6 Removing the menu void remove(void) By calling the remove() function the menu, including all sub-menus, will be removed. 27 CSPANEL 27.1 Purpose The PANEL class is intended to provide data entry screen much like the ones used by dBASE. Class name: PANEL Prototypes: cspanel.h. 27.2 General Information - This class is publicly derived from the window class. This implies that you can use all the functions from the window class to manipulate the panel window. - The default panel will appear both horizontally- and vertically-centered. The destructor of the class will remove, if needed, the panel and all fields. - The maximum number of fields in the panel is set to 35 by a preprocessor constant MAX_FIELDS. This value can NOT be increased. The variable is defined in the CSPANEL.H file. - The fields are numbered. The first field is numbered ZERO. - Fields can be made 'protected'. This means you cannot alter the contents of the field. The protection can be changed between successive calls to the read function (see below). - A 'changed' function is provided to determine whether-or-not something has been altered. - Functions are available to set the EXIT key and the EXIT field(s). 27.3 Public member functions void field_color(int col_att) Sets the default text ATTRIBUTE of each new added field. Meaning: each field which is added to the panel after a call to field_color() gets color col_att, unless this color is explicitly overwritten. int field_color(void) Returns this field attribute. void color(int border,int screen,int field) Sets the colors of the border, the screen and the field. int add_field( int top, int left, int color, int length, TYPE&PARM) This function adds an additional field to the panel. The function returns the number of fields defined so far (including the one you just added.) Parameters: top: The row of the field, relative to the panel. left: The left column of the field, relative to the panel. color: The color ATTRIBUTE of the field. length: The number of positions of the field. TYPE&PARM: One of the following: TYPE PARM char *s For a string field int &i For an integer field. long &l For a long field. float &f For a floating point field. double &d For a double field. int read(void) When the panel is adequately defined, the panel-user can be given control by a call to read(). The function returns when the user hits the exit-key or the ESC key. FALSE is returned if the panel is left by ESC, TRUE otherwise. int changed(void) Returns TRUE if the panel is actually changed during the last read. If nothing is changed, the function returns FALSE. int protect(int ebd) Intended to create a field which can NOT be edited. There are three options: - EDIT The default, the field can be edited. - BROWSE The cursor will not skip this field and you can let the string scroll but are not allowed to alter the contents. - DISPLAY The cursor will skip this field. The initial value will be displayed. This is applied to the last added field. The return value is TRUE if successful, FALSE otherwise. int protect(int FieldNum, int ebd) Same as protect(int ebd) but applied to field FieldNum. int exit_key(int KEY) Sets the key which makes the panel become 'accepted'. The default is ENTER in which case the ENTER key cannot be used to skip from one field to another. The ESC-key will always terminate the panel and will make the read function returns FALSE. The allowable keys are the ones defined in CSKEYS.H. int exit_field(int FieldNum, int YesNo) Normally the panel can be terminated no matter on which field the cursor is placed. After a call to exit_field(), the panel can only be terminated with the cursor standing on a field which is marked as an 'exit-field'. Calling exit_field(FieldNum,TRUE) marks field FieldNum as 'exit-field. Exit_field(FieldNum,FALSE) removes the mark. int exit_field(int YesNo) Same as exit_field(FieldNum,YesNo) except that it is applied to the last added field. void escape_off(void) void escape(int True_of_False) void escape_on(void) Escape_off() or escape(FALSE) disable the escape key. The panel can only be left by the 'exit-key'. Escape_on() or escape(TRUE) enables the escape key. This means the panel can be left by hitting escape. void remove(void) Remove the panel and all fields. All dynamically allocated memory is freed. This is also done by the destructor off the class. void date(int type) All the basic data types are automatically recognised by the add_field function. This is not so for dates. If you want a string to be treated as a date you have to call this function after the add_field() function. 27.4 Dates This paragraph describes the value of 'type' in the date(type) function. There are several different formats supported: Formats and the examples for April/25/1993. The formats are build out of: M Month D Day Y2 Year 2 positions Y4 Year 4 positions FORMAT: EXAMPLE: MDY2 04/25/93 MY2D 04/93/25 Y2MD 93/04/25 Y2DM 93/25/04 DMY2 25/04/93 DY2M 25/93/04 MDY4 04/25/1993 MY4D 04/1993/25 Y4MD 1993/04/25 Y4DM 1993/25/04 DMY4 25/04/1993 DY4M 25/1993/04 EXAMPLE: main(void) { char date_field[]="25/04/93"; PANEL pan; pan.add_field(4,6,date_field); pan.date(MDY2); } 27.5 Fields The fields in the panel are instances of an independent class. They are derived from both the WINDOW class and the EDSTR class. This means that you can apply all the functions from these classes to each field independently. The modify a field you have the use the mod_field macro. EXAMPLE: // Suppose you want to change the color of a // specific field. #include "cspanel.h" main(void) { float balance=-345.87; int c_in_debt=make_color(RED,BLACK); PANEL pan; pan.add_field(2,3,10,balance); if(balance<0) { // display the figures in red if the // balance is negative. pan.mod_field.text_color(c_in_debt); } } The purpose of the macro is to hide a peculiar syntax. The 'pan.mod_field.text_color(c_debt)' is expanded into: pan.field().text_color(c_debt) If you have no objection against a syntax like that you can use it directly. In fact, if you want to change a field other then the last-added you have to use the function: FIELD& field(int num) EXAMPLE: // Suppose you want to put a border around field number // three ( Remember the first field is field ZERO.) PANEL pan; pan.add_field..... // Adding pan.add_field..... // Some pan.add_field..... // Fields pan.add_field..... // pan.add_field..... // pan.field(3).border(BORDER_SINGLE); // Change the color of the fourth field Functions: FIELD& field(void) To change the last added field. FIELD& field(int num) To change field 'num'. Macro: mod_field Same as field(). 27.6 Using the panels A panel basically consists of a window and a number of fields. The fields can be changed by the 'mod_field' macro and the shape and size of the panel-window can be altered with the functions from the WINDOW class. However there is no special function to add normal text in the panel. Of course this is not needed because the normal gotoxy() and cprintf() functions can be used for that. But don't forget to 'activate' the panel before you write text in it. Example: #include "cspanel.h" main(void) { char str[]=" Some string "; char da[]="30/03/1987"; PANEL panel; panel.set_dim(-1,-1,13,55); // These are panel.head(" A demo "); // all functions panel.border(BORDER_DOUBLE); // from the WINDOW class. panel.activate(); // DON'T FORGET THIS !!! // The panel is now visible // (Although without fields) gotoyx(2,10); cprintf("String: "); // Put text in the panel. panel.add_field(2,20,25,str); // Add a field. gotoyx(7,10); cprintf("Date: "); // Put text in the panel. panel.add_field(7,20,da); // Add a date field. panel.date(DMY4); // Date Format. panel.exit_key(F10); // Leave panel with F10. panel.read(); // Edit the data. } // Note: gotoyx() is used instead of gotoxy(). // This is easier because the coordinates of the // add_field() functions also work in that way. 27.7 Data validation At the moment of writing the capabilities for data validation are limited. There exists a option to supply a maximum and a minimum value. Apart from that, there is a 'picture()' function to supply some crude sort of template. It isn't perfect, but it will do in most cases. Without 'anything' the next table applies: date: Are validated correctly, including leap-years. int: Accepts everything between -99999 and 99999. long: Accepts everything between -9999999999 and 9999999999. float and double: No checks for precision underflow or overflow. Accepts exponents between -999 and +999. No limits to the number of digits in the mantissa. string: No limitations. char: Everything of length 1 is accepted. When the user is editing a field, he/she is only allowed to skip to the next field if the value is considered 'valid'. When the 'escape' key is not disabled, the user can leave the panel with invalid values by hitting 'escape'. 27.7.1 Min and Max Syntax: void set_max(...); void set_min(...); Every type of field has its own set of functions to supply a maximum and a minimum value. These are inline functions which accept a type of parameter which may differ from the field type as long as it makes sence. E.g. a field of type 'long' has a set_max() function which accepts a 'int' but does not have a function which accepts a string. // Example Data Validation #include "csa.h" void main(void) { PANEL panel; int value=125; panel.set_dim(-1,-1,19,75); // Centre the panel on the screen. panel.head(" Data Validation "); panel.border(BORDER_DOUBLE); panel.activate(); gotoyx(6,10); cprintf("Integer field "); panel.add_field(6,30,4,value); // Use 4 positions to edit 'value'. panel.mod_field.set_max(399); // Set the maximum value to 399. panel.mod_field.set_min(99); // Set the minimum value to 99. panel.read(); // Do the editing. } 27.7.2 Reset Max & Min Syntax: void reset_max(void); void reset_min(void); When the maximum or the minimum value is no longer required, it can be removed by a call to reset_max() or reset_min(). // Example Reset max & min #include "csa.h" void main(void) { PANEL panel; int value=125; panel.set_dim(-1,-1,19,75); // Centre the panel on the screen. panel.head(" Data Validation "); panel.border(BORDER_DOUBLE); panel.activate(); gotoyx(6,10); cprintf("Integer field "); panel.add_field(6,30,4,value); // Use 4 positions to edit 'value'. panel.mod_field.set_max(399); // Set the maximum value to 399. panel.mod_field.set_min(99); // Set the minimum value to 99. panel.read(); // Do the editing. panel.mod_field.reset_max(); // Reset the maximum value. panel.mod_field.reset_min(); // Reset the minimum value. panel.read(); // Edit again. panel.remove(); // Remove the panel from the screen. } 27.7.3 Templates Templates are used to specify more accuretly the format of the field. This is done by describing the field position-by-position. Next is a table indicating which symbols can be used in the template and what they mean. n 0123456789-+ Blank allowed. N 0123456789-+ Blank NOT allowed. o 0123456789 Blank allowed. O 0123456789 Blank NOT allowed. p 123456789-+ Blank allowed. P 123456789-+ Blank NOT allowed. q 123456789 Blank allowed. Q 123456789 Blank NOT allowed. s -+ Blank allowed. S -+ Blank NOT allowed. A The characters from the alfabet. Blanks NOT allowed. a The characters from the alfabet including blanks, ,.;: etc. B A but displayed as capital. (Not implemented yet.) b a but displayed as capital. (Not implemented yet.) C A or O c a or n Or in a table: ÉÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º ³ 12345 ³ 0 ³ +- ³ blank ³ abcdefghijk ³ ~`!@#$%^ º º ³ 6789 ³ ³ ³ ³ lmnopqrstu ³ &*()_=|\}{[]"º º ³ ³ ³ ³ ³ vwxyz ³ :;'?><,./ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º n ³ x ³ x ³ x ³ x ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º N ³ x ³ x ³ x ³ ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º o ³ x ³ x ³ ³ x ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º O ³ x ³ x ³ ³ ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º p ³ x ³ ³ x ³ x ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º P ³ x ³ ³ x ³ ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º q ³ x ³ ³ ³ x ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º Q ³ x ³ ³ ³ ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º s ³ ³ ³ x ³ x ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º S ³ ³ ³ x ³ ³ ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º a ³ ³ ³ x ³ x ³ x ³ x º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º A ³ ³ ³ ³ ³ x ³ º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º c ³ x ³ x ³ x ³ x ³ x ³ x º ÇÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄĶ º C ³ x ³ x ³ ³ ³ x ³ º ÈÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍͼ A template can be applied to a field by calling the 'picture()' function. Syntax: void picture(char *temp); The template itself is respresented as a string. When a template string is used, only the first 127 characters from the ASCII set can be used (reliable) in editing the field!! Example: #include "csa.h" void main(void) { PANEL panel; int value=125; panel.set_dim(-1,-1,19,75); // Centre the panel on the screen. panel.activate(); panel.add_field(6,30,3,value); // Use 3 positions to edit 'value'. panel.mod_field.picture("QOO"); // A 3 positions integer. No blanks. // The first pos is not a '0'. // No -+ allowed/ panel.read(); // Do the editing. panel.remove(); // Remove the panel from the screen. } When a character other then one from the table, appear in the template string, this character becomes a mandatory setting of the field at that particular position. Example: PANEL panel; char *str; ....... panel.add_field(6,10,9,str); panel.mod_field.picture("tel:AAAAA"); ..... ... In this example the string will be 9 positions of which the first 4 will be 'tel:'. The last 5 will be freely editable, although only the characters from the alfabet are allowed. This setup works fine until you want to use one of the template codes as a literal. To facilitate in that, a 'special' character is supplied. This is the '@' character. E.g.: the template string "phone:AAAAA" will probably not do what you want, because the p,o,n are interpreted according to the table above. You have to use the string "@ph@o@ne:AAAAA" instead. It is possible to supply only a limited number of options at a specific position. For this, there is a special 'or' syntax. Example: ...mod_field.picture("o.oo[e|E]o"); In this example there has to be a 'e' or a 'E' at the fourth position. Nothing else will be allowed. The or syntax takes the form of: [option1|option2]. The "or's" can be nested: Example: ...mod_field.picture("[oooooo|o.oo[e|E]o]"); The length of the options has to be the same. Note that 'e' is of the same length as 'E' and 'oooooo' is 6 positions just as 'o.oo[e|E]o'. More then two options are also allowed: Example: ...mod_field.picture("o.oo[f|F|e|E]o"); 28 Registration Form Name: __________________________________________ Address: __________________________________________ _____________________________________ City/State: __________________________________________ Country: ______________________ Zip: ______________ Telephone: (________) _________ - ____________________ Diskette preference (circle one): 5.25 3.5 If it's the same to you, please pick 3.5. Number Price F Price $ (Dutch Guilders) (American Dollars) CS-Libraries _________ at f 200.- or $ 125.- Total enclosed f ______ or $ _____ What would you like to see added or changed in the CS-Libraries? _____________________________________________________________ _____________________________________________________________ _____________________________________________________________ _____________________________________________________________ _____________________________________________________________ Please, send completed form and check or money order to: Combis P.O. Box 3303 2280 GH RIJSWIJK The Netherlands Note: If this documentation is more then a year old, it is advisable to obtain a later version. Software, price and even address may have changed!