Professional Documents
Culture Documents
Part No: 42-M-JAPRM-20.1 Copyright 1991 - 1997 James Anthony Computing. All rights reserved.
Copyright
All rights to this documentation are reserved. This document contains proprietary information that is protected by copyright. No part of this document may be reproduced, transmitted, or made available directly or indirectly to a third party without the express written agreement of James Anthony Computing. While every effort has been made to ensure a complete and accurate document, no responsibility is expressed or implied for problems arising from any inaccuracy contained herein. Additionally James Anthony Computing reserves the right to make changes to this document and the software described herein at any time and without notice.
Acknowledgements
jBASE, jBC, jED, jSHELL, jLP, jEDI, jCL, jQL, j1, j2 and j3 are trademarks of James Anthony Computing. UNIX is a registered trademark of X/Open Co. Ltd. REALITY is a trademark of MDIS plc. PICK is a trademark of Pick Systems Inc. All other trademarks are acknowledged.
ii
Table of Contents
Using the Manual ....................................................................................................vii Summary of Chapters.........................................................................................vii Notation Conventions ...................................................................................... viii Errata and Comments ...............................................................................................ix Chapter 1: Introduction Introduction to jBASE........................................................................................... 1-1 Run-Time Components .................................................................................... 1-1 Development Components............................................................................... 1-1 Administrative Components............................................................................. 1-1 Requirements for Running jBASE................................................................... 1-1 Application Development...................................................................................... 1-3 Migration ......................................................................................................... 1-3 Executable Paths.............................................................................................. 1-3 C Code Extensions........................................................................................... 1-3 ICONV and OCONV Extensions. ................................................................... 1-3 The IOCTL Function ....................................................................................... 1-3 jEDI Database Drivers..................................................................................... 1-3 jEDI API Calls................................................................................................. 1-4 Makefiles ......................................................................................................... 1-4 jbc and BASIC Commands .............................................................................. 1-4 jBuildSLib and CATALOG Command............................................................ 1-4 Advanced Tools ............................................................................................... 1-4 Notes on Examples .......................................................................................... 1-4 Chapter 2: Migration Overview ............................................................................................................... 2-1 Application Source .......................................................................................... 2-1 Application Security ........................................................................................ 2-2 Application Connectivity ................................................................................. 2-2 Application Interchange................................................................................... 2-2 Application Backup ......................................................................................... 2-3 Exporting Applications.......................................................................................... 2-4 Importing Applications.......................................................................................... 2-5 Importing Accounts ............................................................................................... 2-6 Accountname v. Username .............................................................................. 2-6 User Port Numbers .......................................................................................... 2-6 Generating an Application Account ...................................................................... 2-7 Converting an Application..................................................................................... 2-9 Compiling an Application ................................................................................... 2-10 Cataloging an Application ................................................................................... 2-11 Chapter 3: Execute Paths Introduction ........................................................................................................... 3-1 Creating a Source .................................................................................................. 3-2 Compiling Object Code......................................................................................... 3-3 Creating a UNIX Executable................................................................................. 3-4 Execute the Command........................................................................................... 3-5 Program Execute Paths.......................................................................................... 3-6 Subroutine Execute Paths ...................................................................................... 3-7 Copying Programs and Subroutines ...................................................................... 3-9 Chapter 4: C Extensions Calling C Functions - Overview ............................................................................ 4-1 Simple C Example................................................................................................. 4-2 Fundamental Components of C Interfaces............................................................. 4-3
iii
jBC Supplied Header File ................................................................................4-3 Naming Conventions ........................................................................................4-3 VAR Variable...................................................................................................4-3 INT32 Variable ................................................................................................4-3 FLOAT Variable ..............................................................................................4-4 STRING Variable.............................................................................................4-4 Function Prototypes..........................................................................................4-4 Automatic Type Conversion.............................................................................4-5 C Function Type Conversion............................................................................4-5 C Function Definition.......................................................................................4-6 A More Complex Example ....................................................................................4-7 Register v. User Variables. ....................................................................................4-9 Creation and Destruction of VAR Type Variables...............................................4-10 Supported Functions and Macros.........................................................................4-11 Macros to Store Values into a Variable..........................................................4-11 Variable Type Conversion..............................................................................4-12 VAR Variable Creation and Destruction........................................................4-14 STRING Type Manipulation..........................................................................4-15 Replacing SUBROUTINEs with C Functions......................................................4-16 Compiling and Linking with C Functions. ...........................................................4-18 General Rules and Helpful Hints .........................................................................4-20 Chapter 5: ICONV and OCONV Extensions Introduction............................................................................................................5-1 Conversion Code Extensions .................................................................................5-2 User Exit Code Extensions.....................................................................................5-3 Chapter 6: IOCTL Function Introduction............................................................................................................6-1 JBC_COMMAND_GETFILENAME Command ..................................................6-2 JIOCTL_COMMAND_CONVERT Command .....................................................6-3 JIOCTL_COMMAND_FILESTATUS Command ................................................6-5 JIOCTL_COMMAND_FINDRECORD Command ..............................................6-6 JIOCTL_COMMAND_HASH_RECORD Command ...........................................6-7 JIOCTL_COMMAND_HASH_LOCK Command ................................................6-8 Chapter 7: jEDI Database Drivers Introduction............................................................................................................7-1 Shared Object Database Drivers ............................................................................7-2 File Ajar Processing...............................................................................................7-5 File Descriptor Member ProcessAjar ...............................................................7-5 File Descriptor Member ProcessReopen ..........................................................7-5 jEDI Support Functions. ........................................................................................7-7 Memory Allocation Functions..........................................................................7-7 Record Locking Function .................................................................................7-7 jEDI Base Code................................................................................................7-8 INIT - Initialisation of Database Driver.................................................................7-9 OPEN - Open a File .............................................................................................7-11 CLOSE - Close an Opened File ...........................................................................7-13 SELECT - Select Record Keys from a File..........................................................7-14 SELECTEND - Terminate a Selection Process ...................................................7-16 READNEXT - Get Next Record Key from Selection..........................................7-18 READ - Read a Record from a File .....................................................................7-20 WRITE - Write a Record to a File .......................................................................7-23 DELETE - Delete a Record from a File...............................................................7-25 CLEAR - Delete All Records from a File ............................................................7-26 LOCK - Provide Record Locking Mechanism.....................................................7-28 IOCTL - Support Database Driver Control Functions..............................................7-30
iv Manual Advanced Programmers Reference
SYNC - Synchronise the Data to Disk................................................................. 7-33 Chapter 8: jEDI API Calls Introduction ........................................................................................................... 8-1 Initialisation........................................................................................................... 8-2 Making jEDI Database Requests........................................................................... 8-3 Program Termination............................................................................................. 8-4 Compiling and Linking a Program ........................................................................ 8-5 Finding the #include Files................................................................................ 8-5 Linking with the jEDI Library. ........................................................................ 8-5 Transaction Boundary Support.............................................................................. 8-6 Starting a Transaction ...................................................................................... 8-6 Ending (Committing) a Transaction................................................................. 8-6 Aborting (Rollback) a Transaction: ................................................................. 8-6 Query the Status of a Transaction: ................................................................... 8-7 jEDI Environment Variables ................................................................................. 8-8 JediOpen - Open a File.......................................................................................... 8-9 JediOpenDeferred - Deferred Open..................................................................... 8-10 JediClose - Close an Opened File........................................................................ 8-11 JediSelect - Select Record Keys from a File........................................................ 8-12 JediSelectEnd - Terminate a Selection Process ................................................... 8-13 JediReadnext - Get Next Record Key.................................................................. 8-14 JediReadRecord - Read a Record from a File ..................................................... 8-15 JediWriteRecord - Write a Record to a File ........................................................ 8-16 JediDelete - Delete a Record from a File............................................................. 8-17 JediLock - Provide Record Locking Mechanism................................................. 8-18 JediIOCTL - Database Driver Control Functions................................................ 8-19 JediSync - Synchronise the Data to Disk............................................................. 8-20 JediClearFile - Delete All Records from a File ................................................... 8-21 JediPerror - Print Error Message ......................................................................... 8-22 JediFileOp - General File Maintenance............................................................... 8-23 JediReinitialise - Re-initialise jEDI Code............................................................ 8-25 Chapter 9: Makefiles Introduction ........................................................................................................... 9-1 Chapter 10: jbc and BASIC Introduction ......................................................................................................... 10-1 jbc Command ...................................................................................................... 10-2 jBC Compiler Order of Processing ................................................................ 10-2 How to Use the jbc Command ....................................................................... 10-2 jbc cc Only Options ....................................................................................... 10-3 jbc Common cc and jbc Options.................................................................... 10-3 jbc Only Standard Options............................................................................. 10-4 jbc SQL Options ............................................................................................ 10-5 jbc C++ Options............................................................................................. 10-5 INCLUDE File and Library Processing......................................................... 10-6 Environment Variables and the jbc Command............................................... 10-6 jbc Examples.................................................................................................. 10-7 jpp Macro Pre-processor................................................................................ 10-7 BASIC Command................................................................................................ 10-9 Using the BASIC Command .......................................................................... 10-9 jbc and BASIC Warning Levels ........................................................................ 10-10 Chapter 11: jBuildSLIb and CATALOG Introduction ......................................................................................................... 11-1 jBuildSLib Command.......................................................................................... 11-2 Creating the Input Objects to jBuildSLib ...................................................... 11-3 Using the jBuildSLib Command to Create a Shared Object .......................... 11-3
Referencing a Shared Object from a C or jBC Program.................................11-4 CATALOG Command .........................................................................................11-6 CATALOGing a jBC Program .......................................................................11-6 CATALOGing a jBC Subroutine ...................................................................11-6 Extending the Use of CATALOG ..................................................................11-7 CATALOG Configuration File.......................................................................11-8 Chapter 12: Advanced Tools Introduction..........................................................................................................12-1 jBC Run-time Options..........................................................................................12-2 jPMLMsg Command............................................................................................12-4 Profiling ...............................................................................................................12-5 Enabling Profiling ..........................................................................................12-5 Reporting the Profiling Files ..........................................................................12-5 Example of Profiling ......................................................................................12-6 Debugging - the JBCDEBUGGER Variable........................................................12-8 Terminal Redirection ...........................................................................................12-9 jshow Program ...................................................................................................12-10 Extended terminfo Capabilities..........................................................................12-12 jtic Program..................................................................................................12-12 jtic Description File......................................................................................12-12 Examples of Using jtic .................................................................................12-13 Extended Capabilities...................................................................................12-13 Run-time Error Messages...................................................................................12-17 Modifying Entries in the Error Message File ...............................................12-17 jmakeerr Command ......................................................................................12-18 Index
vi Manual
vii
Notation Conventions
The manual uses the following conventions when describing command syntax: BOLD TEXT Italic text Text in this format represents required user input. The exact characters as shown should be entered at the keyboard. In a syntax specification, italics show parameters that must be entered and supplied by the user. Within descriptive text, italics are used to add emphasis, or to show a term that has been defined elsewhere. CAPITALS {} ... <ENTER> CAPITALS indicate keywords, file names or constants. Braces are used to surround optional parameters within command specifications. The use of ellipses indicates a command parameter that can be repeated as many times as required. Capitals surrounded by angled brackets refer to actual keys on the keyboard. A series of keys inside angled brackets such as <CTRL A>, indicate that the combination of keys should be pressed simultaneously. A vertical line separating arguments means that at least one, if not both arguments must be provided. This style of text represents code or variables.
viii Manual
Technical Publications Department JAC 599 Maxted Road Hemel Hempstead Hertfordshire HP2 7DX England
Tel:
Fax: +44 (0)1442 233 515 email - international: info@jac.co.uk probs@jac.co.uk email - USA: info@jac.com probs@jac.com Please include your name, company, address, phone, fax numbers and your email address if applicable.
ix
Chapter 1: Introduction
Introduction to jBASE
jBASE is an application development and database management system that enhances and extends the UNIX, Windows NT and Windows 95 Operating System. jBASE also allows existing applications to be easily migrated from other DBMS, including PICK and REALITY. jBASE was designed to be, and is, Database Independent. jBASE offers built-in Client/Server abilities, embedded SQL functionality, an easy-to-use spooler, and an easy-to-use enquiry language. The Application Language, jBC is a super-set of Dartmouth BASIC and is ideal for developing business applications. The compiler can optionally produce C or C++ Source code from the original jBC (BASIC) Sources.
Run-Time Components
The run-time components are designed for freedom of choice, efficiency and ease of use. They consist of: The jBASE External Device Interface (jEDI), which provides database independence and distributed processing capabilities; jBASEs own family of NF2 file systems, namely j1 and j2 files; The jBASE query language (jQL).
Development Components
The development tools are designed for highly productive development, simple migration and run-time efficiency of resulting software. They consist of: A set of tools for easy migration to jBASE from existing environments; The jBC Programming Language - a compiled language and a superset of Dartmouth BASIC which allows structured programming. Note that the jbc compiler can also be directed to produce C or C++ source code; A set of development editors and debugging tools; The jBASE job control language (jCL).
Administrative Components
Being an enhancement and extension of the UNIX and the Windows family of Operating Systems, jBASE takes advantage of all the administrative functionality of the host environments. However, there are some extra facilities provided over and above those of the host environment. They include: The jBASE Spooler (jLP) Utility programs such as jbackup and jrestore
1-1
Introduction
The jBASE system has been successfully implemented on Intel, HP, DG, IBM, Digital, Sun, Sequoia and Motorola 88K UNIX based platforms. Other systems (including Windows) are either in development or will be implemented as they are required.
Introduction Manual
1-2
Application Development
As a simple example of application development in a jBASE environment, the developer would perform the following tasks: Possibly migrate an existing application from another environment. Create or modify application source code using a jBASE or UNIX editor. Create object code by using the BASIC command. Make the object code executable by using the CATALOG command. Run and test the application.
There are many extensions to the above that are possible with jBASE. Later chapters discuss these extensions in detail, but a summary of each extension is given below.
Migration
The normal migration path simply involves a save of the application source code and data in one environment, and then restoring and converting it in the jBASE environment. The Migration chapter gives more details of the migration path.
Executable Paths
By default, executable programs will be found in the directory given by the expression $HOME/bin. This directory is where the CATALOG command will create UNIX executables. Any SUBROUTINE objects found by the CATALOG command will be placed in the directory $HOME/lib. The Execute Paths chapter shows how these defaults can be amended to suit your development and live environments.
C Code Extensions
The jBC language provide a rich set of intrinsic functions for the application programmer. There are circumstances when you may want to extend the standard set by writing your own functions in the C language. The C Extensions chapter gives details on how you can do this.
1-3
Introduction
Makefiles
A typical UNIX development environment will make use of a development tool called make. This same facility can be used with jBASE programs and allows a jBASE developer to describe to UNIX the application sources and a set of rules showing how to build the application from the sources. The chapter entitled Makefiles shows how to use this very useful UNIX facility within a jBASE development environment.
Advanced Tools
There are several tools available to the advanced programmer, for profiling support for example. These tools are detailed in the Advanced Tools chapter.
Notes on Examples
Throughout the manual there are many examples of programs being executed from the UNIX shell. All the examples assume the command will be run from the Korn shell (or ksh) program. Of the many shells available to UNIX systems, this is the most popular. Use of the Korn shell is usually denoted by a % (percent) character at the start of the command line. For example: % jbc prog.b -Jo If you use a different shell, such as the C shell or 'csh', you will need to amend the command slightly. For example, the Korn shell (ksh) command: % LD_LIBRARY_PATH=/home/lib prog > progout 2>&1 would look like this under the csh program % setenv LD_LIBRARY_PATH=/home/lib % prog >& progout
Introduction Manual
1-4
Chapter 2: Migration
Overview
Historically, migrating an application from a proprietary environment has proved to be both technically complex and very expensive in terms of resource. Migrating via jBASE can alleviate the majority of the technical complexities thus dramatically reducing the migration period. To effect a smooth and successful migration, the following areas should be given careful consideration. These considerations can then be used to form the basis of an application migration plan. Application source Application security Application connectivity Application interchange Application backup
Application Source
The application source can be categorised as shown below. The logical location of each category should be noted and any associated logistical problems resolved. The most common problem is how to effect a transfer of the application source from one environment to the other. BASIC Source In-house source Code items. Include items. Enabling/protection mechanisms. Third party source. Code items. Include items. Enabling/protection mechanisms. License agreements. Maintenance agreements. System utilisation. User exits. Function anomalies. Language anomalies. PROC Source Any frequently used PROCs provided by the system should be noted, as similar functionality may be required. The type of procedural source style (i.e. PQ or PQN), should be noted for in-house or third party generated source code. In-house source Code items. Enabling/protection mechanisms. Third party source. Code items.
2-1
Migration
Enabling/protection mechanisms. License agreements. Maintenance agreements. System utilisation. User exits. System utilities. Data/Report Retrieval Notes should be accumulated of any retrieval language requirements, especially with regard to the use of user exits and external file cross references. Control Source All control records (e.g. execution control records, etc.) should be set to an initialised or known state before the application is saved for transfer. Any utilisation of existing system files and records (e.g. logon records, etc.) should be recognised and noted. In-house control records. Third party control records. System utilisation. DATA Source The location of data files and records external to, but utilised by, the application should be noted. Once migrated, the application should be able to be fully tested with real test data in order to highlight any anomalies. Before an application can go live, application dependent data files must be transferred from the current source environment. These data file transfers represent the minimum delay period between switching users from the original to the migrated application. It should be recognised that to retain database integrity the source and migrated application should be closed to users for the duration of the application backup period and also for the duration of the restoration period.
Application Security
The current environment for application security should be reviewed with respect to the new environment. This should be undertaken to ensure that the integrity of the application, and the files on which the application depends, cannot be jeopardised by unauthorised users. A view should also be taken as to modifying the existing security arrangements to utilise any new more secure or powerful features not previously available.
Application Connectivity
The application connection methods for both incoming and outgoing application connections by users, background processes, clients and servers should be reviewed with respect to the new environment. Areas that tend to become afterthoughts are modem connections and printing requirements. An appraisal of both system and local printing requirements along with terminal and special device connectivity should be undertaken.
Application Interchange
Any currently employed transfer techniques, which allow interchange of data or control information with external systems or applications, should be fully considered. Magnetic tape media transfers tend to be the most problematic, due to the wide range of manufacturers specifications. Careful attention should be given to understanding the full implications of media mismatch. Interchange problems may still occur even though the interchange media is the same - for example, EOT (end of tape) handling.
Migration Manual
2-2
Application Backup
Consideration should be given to the methods by which the application and associated files are to be secured. Where possible, take advantage of any features not previously available. However, do take care that the integrity of the application database and associated files is preserved while executing backup procedures.
2-3
Migration
Exporting Applications
The majority of application source code tends to reside either within the live application account or in a parallel development account. The following important steps should be executed before the export of an application. 1. Create the source account or source accounts which combine all necessary application source and data files required to create, migrate and test the application. 2. Remove all redundant or experimental source code and include items from the source code. Ensure the source account control files are initialised or set to a known state. The source files should be inspected to ensure that duplicate or old copies of routines do not exist. Any developing code should be moved to independent development source files. Ideally, only the live source code should exist in the source file. Any includes, data records and suchlike should be moved to alternative files. 3. Save the source account in ACCOUNT-SAVE format on a media which is compatible with the target environment. Where possible the SMA save format should be used. Multiple volumes of media should be avoided. Care should be taken to ensure that pointer file or binary items are not included in the application save - the save format for these type of items varies considerably with each supplier, and these items are not required for application migration. The save should be verified where possible to ensure the media can be read back correctly.
Migration Manual
2-4
Importing Applications
The importing of an application consists of the following general steps. Each step will be discussed in more detail later. 1. Restore the source account on to the target machine using the ACCOUNT-RESTORE utility. This utility understands the majority of supplier save formats. 2. Execute the jBASE portation command on all the application basic source files. This command converts any reserved words used as basic variables into capitalised variable names and reformats the code in order to aid portation and maintenance. 3. Compile the application basic source files using the jBASE BASIC command. The BASIC compilation produces a dollar item of the executable in the same file as the source. 4. Once the application source code has compiled successfully, the dollar items can be converted into executable objects by using the jBASE CATALOG command. The CATALOG procedure creates one directory for the main programs and another for any subroutines. 5. The same procedure should be followed for any common general purpose accounts, which contain routines the application depends on. Once all required accounts and routines are migrated, the application should be ready for migration testing.
2-5
Migration
Importing Accounts
Before importing a source account, certain decisions must be made about how the migrated application and any related applications will be used. The following discussions and instructions assume a knowledge of the UNIX environment.
Accountname v. Username
It is likely that the existing application is executed from a single account and that all users logon to the application Accountname. This approach is still possible on the UNIX system, in that all users may login to the same UNIX user id, which would be set to the application Accountname. The application account name or user id would normally be a lower case version of the original account name limited to 8 characters. The reason for lower case is historically related to the way the UNIX login process attempts to handle upper case only devices, by converting upper case to lower case at login time. Once logged in, the users .profile file runs the application start up program. An alternative method is to allocate each user a unique personal user id. UNIX systems are able to implement security on a user id basis. The users may all utilise the same $HOME directory and different users may then be granted individual group and execution permissions - providing useful security checks within the application. Some applications rely on the account name to implement their own security routines or it may be that the account name has been hard-coded within the application. This situation can be handled in jBASE by setting the environment variable, JBCLOGNAME, to the original Accountname. This can be achieved by executing the shell commands below. JBCLOGNAME=ACCNAME ; export JBCLOGNAME
Migration Manual
2-6
2-7
Migration
Each source file is created in the application home directory as a jBASE hash source file. Once all the source files have been successfully restored, the notes of source location should be used to identify the basic source files. Each source file including any files containing includes should be converted by the jBASE PORTBAS utility.
Migration Manual
2-8
Converting an Application
The jBASE PORTBAS command will scan every program and prepare it for the jBASE compiler. There are very few changes required to make the source code compatible with the jBASE compiler. The changes required are executed by the PORTBAS program as follows: 1. A UNIX directory is created and a copy of the original source hash file will be copied to the save directory. The original source file will then remain untouched for reference purposes. 2. Each BASIC program is scanned for keywords used as variable names, which is not allowed in the jBC language. Portbas will not change the variable name in this instance but will capitalise the name. Thus the variable names LOOP, DATE and DATA will become Loop, Date and Data respectively. This allows the program to be read in its original context. 3. The BASIC program is reformatted to aid portation and maintenance. The reformatting is performed so that legacy source code, which used various compression techniques to overcome item size constraints, can be viewed more easily now that item size is not a limiting factor. 4. If the source file is a SUBROUTINE, the name supplied as the subroutine argument is forced to be the same as the source file name. The compiler uses this to name the subroutine rather than the file name. Note: Because of certain ambiguities in the original language specification, it is sometimes impossible for PORTBAS to decide whether the use of a keyword is correct or not. In certain cases it may wrongly decide that a valid keyword is being used as a variable name and change it. However, PORTBAS tends to get it right far more often than it gets it wrong thus saving a great deal of time and effort. The jBASE compiler will trap any incorrect keyword modifications made by the PORTBAS command. As the PORTBAS program executes, it will list the number of the current source item followed by the number of keywords converted. Scripts are available or can be created to utilise the output of the PORTBAS command. Note: Before using the PORTBAS command you should take care to ensure that only basic code exists in the source file. PORTBAS cannot readily distinguish between basic source code and any other type of data. Use the COPY or DELETE commands to remove records that do not contain basic source code. The PORTBAS command will ignore dollar items (those items whose name begins with a $), as these are assumed to contain basic object code which is no longer required. Finally, the PORTBAS command will overwrite the existing basic source item. The conversion phase of the application migration is now complete.
2-9
Migration
Compiling an Application
Before starting to compile the basic source files, any include items required by the basic source code should be located. If the include files reside within another account, that account should be restored under its own home directory. All include file records should have been converted by the PORTBAS command before attempting source code compilation. If include files are located in other directories, links should be created to hashed files in the other directories. For example, MAINPROG is a basic source code item with an include statement: INCLUDE IncludeFile StandardEquates The IncludeFile is located in a general purpose account, now a directory, called anotherdir. A link should be created in the application directory to reference the IncludeFile file in the anotherdir directory. The shell command to create this link is as follows. ln /home/anotherdir/IncludeFile IncludeFile This will create a link in the home directory IncludeFile to the file, IncludeFile, in the directory, anotherdir. Alternately, if you have an MD file defined using the JEDIFILENAME_MD environment variable, you can use a Q pointer to define IncludeFile. Once all include files references have been processed, the application is ready for compilation. The jBASE BASIC command should be used to compile the basic source code programs. The BASIC command should only be executed from the application directory - NOT from an alternate directory which makes use of the link file. The application id should also be used when using the BASIC command. The reason for using the application id and directory is that the .profile script will have set-up the necessary environment variables for the compilation process so that all the permissions will be correct, whereas attempting to compile from another user id or directory may cause very confusing problems later due to incorrect permissions or pathname assignments. The format of the BASIC command is detailed below. BASIC SourceFilename Itemlist The jBASE BASIC command uses a sophisticated set of compilation tools to compile and link jBC basic source code into C object code. The BASIC command will produce a dollar item - an item with the same name as the source item but prefixed by a $. The dollar item will be placed in the same file as the original source item. The BASIC command is a front end to the jbc compiler which is described in detail elsewhere in this document.
Migration Manual
2-10
Cataloging an Application
The jBASE CATALOG command can be used to create UNIX executables and shared libraries for the application source code. The jBASE CATALOG command should be executed from the application directory, rather than using link names, and the application id should be used. The reasons for executing the CATALOG command from the application directory and application id are that the .profile script will have set-up the required environment variables correctly and that the correct file permission will be used when creating and deleting UNIX executables and directories. The format of the jBASE CATALOG command is as follows. CATALOG SourceFilename Itemlist When first invoked, the CATALOG command will create a $HOME/bin directory into which the UNIX executables will be placed. A $HOME/lib directory will also be created into which any subroutines will be placed. The lib directory contains a jLibDefinition file, which describes how to build the subroutines into shared libraries. The entries in the jLibDefinition file are described below: libname naming convention for shared object files.
exportname export list of shared objects. Used as cross reference to find subroutine functions. maxsize maximum size of a shared object library before creating another.
When the maximum size of a shared library object is reached, a new shared library object will be created by the CATALOG command. The new shared library objects are named according to the definition of libname and are numbered sequentially. For example: libname=lib%a%n.so where %a = account or directory name %n = number in sequence. If subroutines were cataloged in the user account name fred, the shared object libraries would be named, libfred0.so, libfred1.so, libfred2.so and so on. Note: To guard against libraries being cataloged incorrectly, under the wrong user account name for example, the definition of libname should be changed to libfred%n.so. This will ensure that any shared objects are created using the proper user account name. The shared library objects (.so files) contain the UNIX executables for subroutine source code. The shared library objects are linked at runtime by the jBASE call function which utilises the dynamic linker programming interface. The dynamic linker will link shared libraries at the start of program execution time, or when requested by the jBASE call function. For example, each executable created using the jBASE compiler will be linked with the jBASE jEDI library functions (libjedi.so) at compilation time. This shared library enables database record retrieval and update. It will be loaded into memory by the dynamic linker when an application executable starts execution. However, the shared library containing any subroutines required by the executing program will only be loaded into memory when initially requested by the subroutine call. Only one copy of any shared library is required in memory at any time, thus reducing program memory requirements. The $HOME/lib directory also contains a directory where all the subroutine objects (.o files) are held. These are required for making the shared library (.so) files. The $HOME/lib directory also contains an export list (.el file), built by the CATALOG command, which is used as a cross reference when dynamically linking shared objects at run time. The main application program executables are placed in the $HOME/bin directory. To enable the application executables to be found, the $HOME/bin path should be added to the PATH environment variable.
2-11
Migration
To enable the executing application to call the correct application subroutines, the JBCOBJECTLIST environment variable should be assigned to the application shared library path $HOME/lib. If the main application program or any subroutine programs make calls to subroutines in other directories, the path of the shared library directories should also be added to the JBCOBJECTLIST environment variable. It is recommended that executables or subroutines of the same name are not available from different directories. This can make application execution very confusing and is reliant on assigning the lib or bin directories to the environment variable in the correct sequence. The assignment of the environment variables should be included and exported in the .profile script file. Executables and shared library objects can be removed from the bin and lib directories by using the DELETECATALOG command. Once all the required application source code has been cataloged, the application can be executed for testing, either from the shell prompt or via a start-up procedure. The jsh will attempt to execute a start-up procedure from the MD file using the JBCLOGNAME environment variable, when invoked with a dash i.e. jsh -
Migration Manual
2-12
Introduction
The usual states for developing a small application can be summarised as: 1. 2. 3. 4. Create a source using ED, jED, vi, emacs or any other editor. Compile the source to object code using the BASIC command. Create a UNIX executable using the CATALOG command. Execute the command.
The above is a gross simplification, but acts as a template to explain the mechanics of the steps involved. Although the above steps are adequate for small applications, they can prove limiting for larger applications or installations. For example, you may want separate development and live environments, a release mechanism, or the ability to share SUBROUTINEs with different accounts and so on. Remember that there are two fundamental source types in the jBC language. The first is the program type, the second is the subroutine type. The subroutine type has the statement SUBROUTINE xxx in the code and is treated as a subroutine rather than a main program. Subroutine sources, when compiled and cataloged, are turned into shared objects that can only be referenced by the CALL statement in another jBC program. Programs on the other hand are turned into UNIX executable programs that can be executed using any of the standard UNIX mechanisms. To start with, the above steps are explained in more detail to reveal the underlying mechanism.
3-1
Execute Paths
Creating a Source
There are two main file types used to contain jBC source, a jBASE hashed file and a UNIX directory. If a jBASE hashed file is used, the source items must be accessed using jBASE tools such as ED and BASIC. Therefore the most flexible file type for containing jBC source is a UNIX directory. The following are examples of creating a source in a UNIX directory, and using the BASIC or jbc command to compile it. Note that when using UNIX directories, it is quite acceptable to use a period . to denote the current directory when using jBASE programs. For example you can LIST the current directory using the command LIST .. Example 1 % % % % % mkdir mysource cd mysource vi prog.b cd .. BASIC mysource prog.b
Example 2 % vi ./prog2.b % BASIC . prog2.b Example 3 % mkdir $HOME/src % JED $HOME/src/prog3.b % jbc $HOME/src/prog3.b -o $HOME/bin/prog3
3-2
3-3
Execute Paths
Following the above steps, there will be an executable UNIX program at $HOME/bin/mainprog and a shared object containing the SUBROUTINE in the directory $HOME/lib.
3-4
3-5
Execute Paths
3-6
3-7
Execute Paths
004 2512\9090]1221\16399 This says that there are 4 subroutines defined, as shown in line 1. The first 2 subroutines SUB1 and DOTHIS are in shared object libpipeman0.so. The second 2 subroutines SUB2 and DOTHAT are in shared object libpipeman2.so. Lines 3 and 4 contain statistical information for use by the CATALOG command.
3-8
3-9
Execute Paths
Example 3 Make subroutines accessible to others. You can allow other users to access your subroutines simply by the user adding the directory of your subroutines to their JBCOBJECTLIST environment variable. For example, if user d4live wanted to access the subroutines in account dev while running program smokeup they could do this: % JBCOBJECTLIST=~dev/lib:$JBCOBJECTLIST smokeup or alternately, if you want to access subroutines in a number of directories: % % % % % JBCOBJECTLIST=$HOME/lib JBCOBJECTLIST=~dev/lib:$JBCOBJECTLIST JBCOBJECTLIST=$JBCOBJECTLIST:~supp/com/lib export JBCOBJECTLIST smokeup
In this example the final components of JBCOBJECTLIST will be ~dev/lib, $HOME/lib and ~supp/com/lib. This is the order that subroutines will be searched for. If duplicate subroutine names be found, the first occurrence takes precedence and the duplicates will be ignored. Example 4 Copy subroutines to another live directory. This entails copying all your subroutines to another directory. The example assumes that subroutines in a development account are being copied to a live account - from the lib directory of account dev to the lib directory of account live. You must copy not only the shared object, but also the descriptor file - the files that have a .el suffix. % find ~dev/lib \( -name *.so -o -name *.el \) -exec cp {} ~/live/lib \; As for Example 2 above, you must not overwrite the shared object if a user is using the shared objects. Again, some versions of UNIX allow the copy to proceed and the final result is a segmentation violation, other versions of UNIX will make the copy fail. Use the same techniques as described earlier for copying programs, e.g. INHIBIT-LOGONS, LOGOFF, ENABLE-LOGONS. A common mistake made during this procedure is the naming of the shared objects. When using the CATALOG command, the name of the account is relevant. For example, if you were to have a number of subroutines that have been made into shared objects using the CATALOG command, in account dev, you might also have files libdev0.so, libdev1.so, libdev.el. Now assume that the account live also has the same subroutines that have been made into shared objects in the live account using the CATALOG command. The names of these files would be liblive0.so, liblive1.so and liblive.el. Therefore, when you copy the files from account dev to account live, they would not over-write whatever is there because the names of the files differ. Instead, they would complement the existing files. Because of these duplicates, it is impossible to say which would be the first copy accessed when a jBC program that accesses subroutines in the live account is run. Example 5 Copy selected subroutines to a common live directory. When you use the CATALOG command, it puts the object code into a shared library. For performance reasons, it will build a number of small shared libraries, rather than one large shared library. This means that when you CATALOG an object you cannot be sure which shared library is has gone into in the $HOME/lib directory, or what other subroutines are also in the library. There may be occasions when you want to selectively copy objects to another destination. For example, you may have a SECURITY subroutine that is common to all applications on your system, that resides in an account called security. You have a new version of SECURITY that you have CATALOGed and tested from account dev. The basic steps are: Log into the target account, in this example the target account name is security. % su - security Password:
3-10
Change directory to where the objects were stored during the CATALOG procedure. This is in directory lib/objsecurity. Note these are standard archive objects, not shared objects. % cd lib/objsecurity Copy the new version of the object from the dev account to the security account. Note that in this case the cp command uses the copy that was created by the CATALOG command - you could just as easily use the copy created by the BASIC command. % cp ~dev/lib/objdev/SECURITY.o . Re-CATALOG the object. % CATALOG . SECURITY.o
3-11
Execute Paths
Chapter 4: C Extensions
Points 2 and 3 are addressed in later chapters. This chapter deals with the most commonly used form of extending the jBC language - calling external C functions. There are a number of reasons why you might want to call an external C function rather than write the functionality directly in jBC: Performance. Although jBC is highly tuned for the language, there is no doubt that under certain circumstances re-writing application functionality in a C function can yield impressive results. An example of this is when code needs to examine each character (or even each bit) of a string of information and perform different functionality according to the character or bit detected. You might need to do this to parse a complex string or calculate CRC checksums for example. Functionality. There are some things that the jBC language just does not cater for, such as OS system calls and communication functions. External libraries. The mechanism provides a means of calling functionality written by other development environments. For example, a jBC application may want to call a geographical database package written in C. There are an equal number of reasons why you should not write C functions. Digest the list below before rushing headlong into writing C functions just because it seemed like a good idea at the time. Portability. Writing C code using an ANSI C compiler provides quite good portable code. However, quite good isnt the same as completely portable. The programmer will need to write C functions with portability in mind. Many books have been written on the subject of writing portable C code, and most competent system level C programmers will already be knowledgeable in this area. Amongst the common reasons for lack of portability are: Byte ordering can differ between machines of different architecture. Differences between signed and unsigned arithmetic. Compiler defaults for undefined aspects of C such as order of precedence in equal precedence statements. The availability and functionality of library function calls. Speed of writing. It will take considerably longer to write the same functionality in C than it will in jBC. Performance. Care must be taken not to actually decrease performance. While writing in C will generate good results for some operations, other operations may provide negligible or even worse results. For example, if you perform a lot of database operations, the time spent in the C code may be minimal compared to the time spent actually performing the database operation, so the overall improvement is negligible. Also, as jBC has been highly tuned for string manipulations and storage allocations, doing the same thing in a C function is not only re-inventing the wheel, but will take a lot of care to give the same performance as jBC code. Maintainability. A jBC program is much easier to maintain than a C function. It is therefore recommended that writing C functions be limited to fairly small functions when the equivalent functionality cannot be written in jBC, or when a small, often used code subset can yield high performance gains, or when interfacing to external third party software.
4-1
C Extensions
Simple C Example
A very simple example of calling an external C function would be: DEFC getpid() PRINT The process ID for this program is : getpid() In this simple example, the first line provides the compiler with information about the external C function. The second line simply uses the function in the same way it would any other intrinsic function, such as COUNT() or INDEX(). A further modification to the above example could be: DEFC INT getpid() pid = getpid() pid += 100 PRINT The process id + 100 = : pid Again, the first line defines the function but this time explicitly tells the compiler that an integer value will be returned (this is the default). The second line assigns the variable pid to the return value from the getpid() function call. In this example we are calling a standard C library function and no further source code is required. It can simply be compiled and executed, for example: % jbc prog1.b -o prog1 % prog1 The process id + 100 = 20678 That is really all you need to know in order to write complex C functions that can be called by a jBC application. The remainder of this chapter explains the interfaces between the jBC application and the C function, and the ability of the C function to utilise standard jBASE macros and functions. If your C functions are going to be isolated from jBC, such as when interfacing to a third party software package, most of information in the remainder of this chapter is unnecessary.
C Extensions Manual
4-2
Naming Conventions
The supported jBASE functions and macros follow a simple naming convention which follows the format FUNCNAME_RPPPPP where FUNCNAME describes the functionality, R describes the return value from the function/macro and P describes any number of parameters to be passed to the function/macro. The values for R and P can be any of the following: I 32 bit integer F Floating point value in jBASE floating format B The address of a jBASE VAR structure. V Void S An array of STRING type characters that are not necessarily 0 terminated. For example, the macro COUNT_IBB is provided. This shows the functionality belongs to the COUNT function, the function returns a 32 bit integer and requires 2 parameters, both of them the address of a jBASE VAR structure.
VAR Variable
This is the definition of the fundamental variable used throughout a jBC program. It has a complex structure, and definitions have been provided for access to the various elements. The VAR variable is a typeless variable and can assume many types, such as an INT32 integer, a FLOAT floating point value and so on. There are many function calls and macros provided to convert between these types, to access these types within a VAR variable, or to update a VAR variable with various types. These are all documented later.
INT32 Variable
This is a definition of a 32 bit integer used internally by jBC. This provides portability with architectures that use 64 bit integers. Therefore, when interfacing with jBC variables, always use the INT32 definition instead of int.
4-3
C Extensions
FLOAT Variable
This is a definition of a floating point value used internally by jBC. The format is not exactly as a C programmer might expect, and is subject to change in future versions of jBASE. As long as the C function uses the definitions and macros supplied with jBASE for the FLOAT variable, future compatibility is assured. You cannot manipulate these floating point values in the same way that you can manipulate C floating point values. For example, if you wish to multiply two FLOAT variables, then instead of using the * operator in a C function, you should use the supplied MUL_FFF macro to perform the operation.
STRING Variable
This is a definition of an unsigned character. The usual format within a C function is to use the STRING * type, which is a pointer to an array of unsigned characters. The array may or may not be terminated with a 0 (null) character. The char * type is a pointer to an array of signed characters, and STRING * is a pointer to an array of unsigned characters. You can usually cast one to another freely in order to avoid compilation warning messages. For example: char buffer[128] ; sprintf(buffer,KEY_%d,rand()); STORE_VBS(Var, (STRING*) buffer); In the above example, sprintf() requires a pointer to an array of signed characters, whereas STORE_VBS requires a pointer to an array of unsigned characters. Usually these are transposable and can be cast. The reason for using unsigned character arrays is that within code it is easier and more correct to use unsigned characters when dealing with values above 127, such as delimiters and attribute marks. For example, the following code is incorrect: char buffer[128] ; buffer[0] = 254 ;
Function Prototypes
For each different external C function you want to reference, there must be a description of the function provided to the compiler. These descriptions are called function prototypes. They tell the compiler the name of the function, the return type from the function, and the type of each parameter passed. For example, consider a C function which has 3 parameters (2 integers and 1 VAR), and returns an integer. The jBC code might look something like this: DEFC INT MyFunc(INT, INT, VAR ) V1 = 12 V2 = 34 V3 = Where are my pipe and slippers ? PRINT Result of MyFunc = : MyFunc(V1, V2, V3 ) The following keywords can be used in the function prototype in a jBC program to define the return value and parameters that the C function expects: INT A 32 bit integer. Although INT is the keyword used in the function prototype, in the C source code you would use the INT32 type.
STRING A pointer to an array of STRING types, the last element in the array being a 0 (null). Although STRING is the keyword used in the function prototype, in the C source code you would normally use STRING * to show it is a pointer to an array of STRING characters. VAR A pointer to a jBC variable, which can be a user variable or register variable (the differences are noted later). Although VAR is the keyword used in the function prototype, in the C source code you would normally use VAR * to show that it is a pointer to a VAR type. Within this complex typeless structure can be INT32 variables, or FLOAT variable and so on. A range of function and macros are detailed later to extract and insert these types from and into the VAR structure.
In the above example, Var1 will become a variable of type INT32 ( a 32 bit integer), Var2 will become a variable of type FLOAT (a floating point unit) and Var3 will become a variable of type STRING * (an array of unsigned characters). The last line, where Var4 is assigned a value of type FLOAT means Var1 will have to be converted from type INT32 to type FLOAT and Var3 converted from type STRING * to type FLOAT before the calculation and subsequent assignment can occur. This type conversion is handled automatically by the compiler. Similar type conversion is handled by the compiler when calling C functions. Consider the following: DEFC INT MyFunc(STRING, INT) Var1 = 12 Var2 = 123 PRINT The result of MyFunc is : MyFunc(Var1, Var2) In this example, Var1 is initially assigned as an INT32 of value 12, and Var2 will be assigned as a STRING * of value 123. This conflicts with the types that the function MyFunc is expecting. So, prior to the function being called, the compiler ensures that the INT32 with a value 12 will be converted to a type STRING * with a value 12 and that the STRING * type with a value 123 will be converted to an INT32 type of value 123. Finally, the return from MyFunc is a 32 bit integer, whereas the PRINT and the string concatenation require STRING types -- again the compiler will force the conversion.
4-5
C Extensions
C Function Definition
Once a function has been prototyped in a jBC source, the C function definition usually has to follow the same pattern. For example, if the jBC source contains: DEFC MyFunc(INT, VAR, STRING) The C function might look something like this: #include <jsystem.h> INT32 MyFunc(INT32 V1, VAR * V2, STRING * V3) { /* */ } There is an exception to this rule when the return type is VAR. In the C language the structure can be returned, but C does not have the flexibility of say, C++ for variable construction and destruction which we need. So, when the return type is VAR, the first parameter passed to the C function is actually the address of a variable (allocated by the compiler) that the function should amend as if it were the return variable. For example, if the jBC source contains: DEFC VAR MyFunc(INT) PRINT Result of MyFunc(3) is :MyFunc(3) The C code would look something like this: #include <jsystem.h> VAR * MyFunc(VAR * result, INT32 param) { char buffer[128] ; sprintf(buffer,MyFunc[%d],param); STORE_VBS(result, (STRING*) buffer); return result ; }
C Extensions Manual
4-6
4-7
C Extensions
In the program prog2.b, the user enters variable FieldQty. Following an INPUT statement the variable will be of type STRING. However, the function assumes an INT32 will be passed, so automatic type conversion occurs. Should the user not have entered an integer, an error message is displayed at run-time and the jBC debugger is entered (the program could always test for this using the NUM() function). Should the user have entered a floating point value such as 3.4, this will be truncated to an integer.
C Extensions Manual
4-8
4-9
C Extensions
C Extensions Manual
4-10
4-11
C Extensions
DEFC VAR xor2(FLOAT, FLOAT) INPUT int1 INPUT int2 res = xor2(int1, int2) PRINT The first value is : OCONV(int1,MCDX) PRINT The second value is : OCONV(int2,MCDX) PRINT The result is : OCONV(res,MCDX) The C function source would look like this: #include <jsystem.h> VAR * xor2(VAR * result, INT32 int1, INT32 int2) { return STORE_BBI(result, int1 ^ int2) ; } STORE_VBI(Target, Numeric) Same as STORE_BBI except a void is returned. STORE_BBS (Target, String) Store a 0 terminated array of characters into a VAR type variable. Consider the following example where we generate two record keys, each with a random element based on a seed value passed. The jBC source may look like this: DEFC MakeKeys(VAR, VAR, INT) PRINT "Enter seed : ": INPUT Seed MakeKeys(Key1, Key2, Seed) PRINT "Record key 1 = " : Key1 PRINT "Record key 2 = " : Key2 The C source would look something like this: #include <jsystem.h> INT32 MakeKeys(VAR * Key1, VAR * Key2, INT32 Seed) { char buffer[128] ; sprintf(buffer,"key_cust_no*%d",rand() % Seed); STORE_BBS(Key1, (STRING*)buffer); sprintf(buffer,"product/*%d",rand() % Seed); STORE_BBS(Key2, (STRING*)buffer); return 0; } STORE_VBS (Target, String) Same as STORE_BBS except a void is returned.
C Extensions Manual
4-12
CONV_FB(Source) Given the address of a VAR type BASIC variable, perform type conversions on the variable and return the FLOAT value. If the Source variable contains a value that cannot be converted, such as a non-numeric STRING, a file descriptor, or a select list, an error message is displayed and the debugger is entered. If the variable is already a FLOAT, a simple extraction takes place. CONV_IB(Source) Given the address of a VAR type BASIC variable, perform type conversions on the variable and return the INT32 value. If the Source variable contains a value that cannot be converted, such as a non-numeric STRING, a file descriptor, or a select list, an error message is displayed and the debugger is entered. If the variable is already an INT32, a simple extraction takes place. Consider the previous example where we generate two record keys, each with a random element, based on a seed value passed. This time the third parameter is passed as a VAR pointer instead of an INT32, and the MakeKeys function has to perform its own type conversion. The jBC source may look like this: DEFC MakeKeys(VAR, VAR, VAR) PRINT "Enter seed : ": INPUT Seed MakeKeys(Key1, Key2, Seed) PRINT "Record key 1 = " : Key1 PRINT "Record key 2 = " : Key2 The C source would look something like this: #include <jsystem.h> INT32 MakeKeys(VAR * Key1, VAR * Key2, VAR *Seed) { char buffer[128] ; INT32 SeedConv ; /* * Convert the STRING in Seed to an integer. */ SeedConv = CONV_IB(Seed) ; sprintf(buffer,"key_cust_no*%d",rand() % SeedConv); STORE_BBS(Key1, (STRING*)buffer); sprintf(buffer,"product/*%d",rand() % SeedConv); STORE_BBS(Key2, (STRING*)buffer); return 0; } CONV_SB(Source) Ensures a variable is of type STRING and returns the address of the first character of the array of characters. Use CONVLEN_IB to find the length of the string. If the type was one of the numeric types, a type conversion will take place as well. For example, an INT of 123 will be converted to a STRING of 123. If the type was neither a STRING, nor any type that could be converted to a STRING (such as a file descriptor or a select list), an error message is displayed and the debugger entered. If the user then enters c to continue, this function will convert Source to a 0 length string. CONVLEN_IB(Source) Similar to CONV_SB except instead of returning the address of the start of the string, it returns the length of the string. CONV_SFB(Source) Similar to CONV_SB except the STRING will be terminated with a 0 (null) character. This is often useful for passing as a parameter to other C functions that depend on 0 terminated strings. The following example shows a C function returning 1 if a UNIX file exists, or 0 if it does not exist. First the jBC code DEFC Exists(VAR) PRINT "Enter UNIX file name to test : ": INPUT FileName
4-13
C Extensions
IF LEN(FileName) AND Exists(FileName) THEN PRINT "File exists" END ELSE PRINT "File does not exist" END Now the C function: #include <jsystem.h> INT32 Exists(VAR * FileName) { struct stat si ; return (!lstat((char*)CONV_SFB(FileName),&si); } CONVTYPE_IB(Source) This returns the type of the jBC variable. The different types that can be returned include: VAR_TYPE_FLOAT VAR_TYPE_INT VAR_TYPE_FLOAT_INT VAR_TYPE_STRING VAR_TYPE_FILE VAR_TYPE_SELECT The variable is in the format of a FLOAT. The variable is in the format of an INT32. The variable is in both the FLOAT and INT32 formats. The variable is a STRING type. The variable is an opened file descriptor. The variable is a list of record keys, or select list.
/* More C code */ /* Tidy up the allocated variables */ STRING_RELEASE_REG_VB(&Var); return 0; } In this example, Var is created as a register variable of type STRING of length 10 bytes. Just prior to the function returning, the STRING_RELEASE_REG_VB is used. However, as it is only a small string, the space will not be released. Thus every time the function is called, space will be lost. There are two ways to avoid this conflict. Firstly, use the STRING_RELEASE_EXT_VB definition instead of the STRING_RELEASE_REG_VB. This will release all space used by the variable. Secondly, dont always initialise the variable by making it static, as in: INT32 MyFunc() { static VAR Var ; if (CONVTYPE_IB(&Var) == 0) { STRING_INITIALISE_REG_VB(&Var) ; } STORE_VBS(&Var, (STRING*) 1234567890); /* More C code */ /* Tidy up the allocated variables */ STRING_RELEASE_REG_VB(&Var); return 0; }
4-15
C Extensions
SUBROUTINE_DATA(4) SUBROUTINE_INIT(&StackData, GlobalBasicVars, &JBCLevel, SubroutineArgs, SubroutineArgsFlags, ActualFlags, "VVVV", _jb_p1, _jb_p2, _jb_p3, _jb_p4); SUBROUTINE_INIT_COMMON MCAT_BBI(_jb_p4, 3, _jb_p1, _jb_p2, _jb_p3); RETURN_V; _jl_main_end: _jl_function_end: SUBROUTINE_END RETURN_SWITCH PROGRAM_END The above C source can be used as a full replacement for the jBC source code equivalent. This is because the compilation of jBC source code involves translating to C code and then compiling the C code. All we have done is to intercept the two stages, and will just carry on with stage 2 in future. However, the above code can be stripped further leaving just the following: #include <jsystem.h> JBC_MyFunc(ActualFlags, _jb_p1, _jb_p2, _jb_p3, _jb_p4) char * ActualFlags; VAR * _jb_p1; VAR * _jb_p2; VAR * _jb_p3; VAR * _jb_p4; { MCAT_BBI(_jb_p4, 3, _jb_p1, _jb_p2, _jb_p3); return 0 ; } The additional parameter ActualFlags is a 0 terminated string detailing what parameters the calling program is actually passing, and can be used to ensure the calling program passes four parameters. In our example, the variable ActualFlags should point to a string VVVV. If it doesnt, the calling program has passed an invalid number of parameters.
4-17
C Extensions
Compiling sources with libraries. You would create a pool of functions and allow the linker to decide which objects to link with the jBC code. For example: % jbc -Jamylib.a func[12].c % jbc prog1.b mylib.a -o prog1 An alternate mechanism for achieving exactly the same result as above is: % % % % % % jbc -c func1.c jbc -c func2.c jbc -c prog1.b ar -r mylib.a func1.o ar -r mylib.a func2.o jbc prog1.o mylib.a -o prog1
Now an example of a main program, one SUBROUTINE written in jBC, and two C functions, all linked together: % jbc prog1.b sub1.b func1.c func2.c -o prog1 -JLS During a normal CALL statement execution, the jBC code will look in all the defined shared objects to resolve the SUBROUTINE call. However, the -JLS option means the SUBROUTINE must be statically linked with the program, which it is. All the above examples result in the C functions being linked statically with the program. Thus, if you have ten programs all using a common C function, there will be ten copies of the function linked with the programs. This technique has a number of drawbacks: If all ten programs are run simultaneously, there will be ten copies of the function in main memory. If the C function is changed, all ten programs need to be re-linked (not too difficult if you have created a suitable make file). Any SUBROUTINEs that call the C functions must also be linked in statically. The way round the above limitations is to create shared objects of the C functions. This topic is discussed in greater detail in the jbc and BASIC chapter. However, as a small example, the following shows how two C function sources can be generated into a shared object: % jbc -c func1.c func2.c % jBuildSLib func[12].o -o myfunc.so The main program that references the two shared objects can now be compiled by referencing the shared object like this: % jbc prog1.b -o prog1 myfunc.so Before the program prog1 can be executed, the shared object needs to become known to the operating system. This can be done in two ways:
C Extensions Manual
4-18
The first is to update the environment variable LD_LIBRARY_PATH (or LIBPATH if using AIX) to point to the directory that the shared object resides in. For example: % export LD_LIBRARY_PATH=`pwd` : $LD_LIBRARY_PATH The second is to copy the shared object to a directory already pointed to by LD_LIBRARY_PATH (or LIBPATH if using AIX). For example: % cp myfunc.so $JBCRELEASEDIR/lib
4-19
C Extensions
C Extensions Manual
4-20
STRING_INITIALISE_USER_VB(&WorkVar); STORE_VBS(&WorkVar, (STRING*)"/home2" ); rc = CHDIR_IB(&WorkVar); STRING_RELEASE_EXT_VB(&WorkVar); return rc ; } Always use unsigned char definitions (STRING types) when dealing with character arrays that may contain system delimiters. Be wary of different environments that require different definitions. For example, to look for an attribute mark in a string, some environments require: ptr = memchr(start, 0xfe, len); Whereas other environments require: ptr = memchr(start, (char) 0xfe, len);
4-21
C Extensions
Introduction
The OCONV and ICONV functions, built into the jBC language, provide a means of performing conversions to strings of data. The OCONV function is used for Output conversions where an internal representation of the data is converted to one more understandable by a user. The ICONV function is used for Input conversions where formats entered by the user are converted into an internal representation. An example of an OCONV would be to convert a time in internal format to an external representation, like this: PRINT OCONV(TIME(), MTS) This will cause the current time to be printed in the format HH:MM:SS. Similarly, the code INPUT CurrentTime ITIME = ICONV(CurrentTime, MTS) allows the user to enter the time in HH:MM:SS format, and the ICONV will convert it to an internal representation in the variable ITIME. A full list of these conversion codes is available in the Programmers Reference Manual. Historically, an application developer has been able to write specialised functionality in assembler code and execute this assembler code through a mechanism known as a user exit. This takes the following form: OutputData = OCONV(InputData,Uxxxx) InputData = ICONV(OutputData,Uxxxx) The value xxxx is a 1 to 4 digit hex value that describes the location of the user exit code. With jBASE it is possible to extend the scope of the conversion codes and user exits. This should really only be done for legacy code - any new code written should use the C function interface, documented in an earlier chapter. In both cases, the extensions are made possible by writing a SUBROUTINE with a name conforming to a specific naming convention. In the examples shown, we always use the jBC language. However, these extensions can be written as C code instead of jBC code. The section Replacing SUBROUTINEs with C Functions in the C Extensions chapter explains this mechanism. Should any extensions to conversion codes or user exits clash with those provided by jBASE, the jBASE version will take precedence. For example, you cannot write your own version of the existing MT conversion code.
5-1
OCONV Extensions
source This is the original variable passed to ICONV or OCONV that is to have the conversion performed upon it. code type error This is the actual conversion code specified in the ICONV or OCONV call. In the above example it will be BF. This is set to 0 if the SUBROUTINE is called as an ICONV, or to 1 if the SUBROUTINE is called as an OCONV. This can be updated in the event of an error. By default, jBASE will assume that the conversion is handled correctly by the user-written extension. In the event that the conversion code is also unknown to the user-written extension, this variable can be set to non-zero causing a conversion error to be displayed and the debugger to be entered.
5-2
5-3
OCONV Extensions
Introduction
The jBC language provides an intrinsic function called IOCTL that behaves in a similar manner to the C function ioctl(). Its purpose is to allow commands to be sent to the database driver for a particular file, and then to receive a reply from the database driver. As with the C function ioctl, the use of IOCTL is highly dependent upon the database driver it is talking to. Each database driver may choose to provide certain common functionality, or may add its own commands and so on. This is especially true of user-written database drivers. First, an example of a jBC source that opens a file and finds the type of file: INCLUDE JBC.h OPEN MD TO DSCB ELSE STOP 201,MD status= IF IOCTL(DSCB,JIOCTL_COMMAND_FILESTATUS,status) THEN PRINT Type of file = :DQUOTE(status<1>) END ELSE PRINT IOCTL FAILED !! unknown file type END If the ELSE clause is taken, it does not necessarily mean there is an error, it only means that the database driver for file MD does not support the command that was requested from it. The file JBC.h is supplied with jBASE in the directory $JBCRELEASEDIR/include. If the source is compiled with the jbc or BASIC command, this directory is automatically included in the search path and no special action is needed by the programmer for the INCLUDE JBC.h statement. The format of the IOCTL function is: IOCTL(filevar, command, parameter) Where: filevar is an variable that has had a file opened against it using the OPEN statement. However, if you want to use the default file variable, use -1 in this position. For example:
OPEN MD ELSE STOP filevar = -1 IF IOCTL(filevar,JIOCTL_COMMAND_xxx,status) ...
command can be any numeric value (or variable containing a numeric). However, it is up to the database driver to support that particular command number. The remainder of this chapter describes the common IOCTL command numbers supported by the jBASE database drivers provided. status pass here a jBC variable. The use of this variable depends upon the command parameter, and will be described later for each command supported.
The return value is 0 for failure, or 1 for success. A value of -1 generally shows the command has not been recognised. This remainder of this chapter will deal with the IOCTL commands that are supported by the provided jBASE database drivers, and the JBC_COMMAND_GETFILENAME command that is supported for all database drivers.
6-1
IOCTL Function
JBC_COMMAND_GETFILENAME Command
Using this command to the IOCTL function, you can determine the exact UNIX file name that was used to open the file. This is helpful because jEDI uses Q pointers, F pointers and the JEDIFILEPATH environment variable to actually open the file, and the application can never be totally sure where the resultant file was really opened. Normally of course, this is of no concern to the application. Example Open the file CUSTOMERS and find out the exact UNIX path that was used to open the file. INCLUDE JBC.h OPEN CUSTOMERS TO DSCB ELSE STOP 201,CUSTOMERS filename = IF IOCTL(DSCB,JBC_COMMAND_GETFILENAME,filename) ELSE CRT IOCTL failed !! ; EXIT(2) END PRINT Full file path = :DQUOTE(filename) This command is executed by the jBC library code rather than the jEDI library code or the database drivers, so it can be run against a file descriptor for any file type.
6-2
Advanced
Programmers
Reference
JIOCTL_COMMAND_CONVERT Command
Some of the jBC database drivers will perform an automatic conversion of the input and output record when performing reads and writes. An example of this is when writing to a UNIX directory. In this case, the attribute marks will be converted to new-line characters and a trailing new-line character added. Similarly for reading from a UNIX directory the new-line characters will be replaced with attribute marks, and the trailing new-line character will be deleted. The above example is what happens for the database driver for UNIX directories. It assumes by default that the record being read or written is a text file and that the conversion is necessary. It tries to apply some intelligence to reading UNIX files, as text files always have a trailing new-line character. Therefore, if a UNIX file is read without a trailing new-line character, the database driver assumes the file must be a binary file rather than a text file, and no conversion takes place. This conversion of data works in most cases and usually requires no special intervention from the programmer. There are cases however, when this conversion needs to be controlled and interrogated, and the IOCTL function call with the JIOCTL_COMMAND_CONVERT command provides the jBASE database drivers that support this conversion with commands to control it. The call to IOCTL, if successful, will only affect file operations that use the same file descriptor. Consider the following code: INCLUDE JBC.h OPEN MD TO FILEVAR1 ELSE ... OPEN MD TO FILEVAR2 ELSE ... IF IOCTL(FILEVAR1,JIOCTL_COMMAND_CONVERT,RB) .. In the above example, any future file operations using variable FILEVAR1 will be controlled by the change forced in the IOCTL request. Any file operations using variable FILEVAR2 will not be affected and will use the default file operation. Input to the IOCTL is a string of controls delimited by a comma that tell the database driver what to do. The output from the IOCTL can optionally be a string to show the last conversion that the driver performed on the file. The description of the available controls that can be passed as input to this IOCTL function are: Control code "RB" "RT" "RI" "RS" "WB" "WT" "WI" "WS" "KB" "KT" "KI" "KS" Description All future reads to be in binary (no conversion) All future reads to be in text format (always do a conversion) All future reads to decide themselves whether binary or text Return to caller the status of the last read ('B' = binary, 'T' = text conversion) All future writes to be in binary (no conversion) All future writes to be in text format (always do a conversion) All future writes to decide themselves whether binary or text Return to caller the status of the last write ('B' = binary, 'T' = text conversion) All future reads/writes have the record key unaltered (no record key conversion) All future reads/writes have the record key modified (always do a conversion) All future reads/writes to decide if to do a conversion Return to caller the status of the last record key conversion ('B' = binary, 'T' = text conversion)
6-3
IOCTL Function
Example 1 The application wants to open a file, and to ensure that all reads and writes to that file are in binary, and that no translation such as new-lines to attribute marks are performed. INCLUDE JBC.h OPEN FILE TO DSCB ELSE STOP 201,FILE IF IOCTL(DSCB,JIOCTL_COMMAND_CONVERT,RB,WB) ELSE CRT UNABLE TO IOCTL FILE FILE ; EXIT(2) END Example 2 Read a record from a file, and find out if the last record read was in text format (were new-lines converted to attribute marks and the trailing new-line deleted), or in binary format (with no conversion at all) INCLUDE JBC.h OPEN . TO DSCB ELSE STOP 201,. READ rec FROM DSCB,prog.o ELSE STOP 202,prog.o status = RS IF IOCTL(DSCB,JIOCTL_COMMAND_CONVERT,status) THEN IF status EQ T THEN PRINT prog.o read in TEXT format END ELSE PRINT prog.o read in BINARY format END END ELSE CRT The IOCTL failed !! END
6-4
Advanced
Programmers
Reference
JIOCTL_COMMAND_FILESTATUS Command
The JIOCTL_COMMAND_FILESTATUS command will return an attribute delimited list of the status of the file to the caller. Attribute <1> <2> <3> <4> <5> <6> <7> <8> <8,1> <8,2> <8,3> <9> Description File type, as a string. FileFlags, as decimal number, showing the LOG, BACKUP and TRANSACTION permissions. BucketQty, as decimal number, number of buckets in the file. BucketSize, as decimal number, size of each bucket in bytes. SecSize, as decimal number, size of secondary data space. Restore Spec, a string showing any restore re-size specification. Locking identifiers, separated by multi-values. FileFlags showing permissions. LOG, BACKUP and TRANSACTION
Set to non-zero to suppress logging on this file. Set to non-zero to suppress transaction boundaries on this file. Set to no-zero to suppress backup of the file using jbackup. Hashing algorithm used.
Example 1 Open a file and see if the file type is a UNIX directory. INCLUDE JBC.h OPEN .. TO DSCB ELSE STOP 201,.. status = IF IOCTL(DSCB,JIOCTL_COMMAND_FILESTATUS,status) ELSE CRT IOCTL failed !! ; EXIT(2) END IF status<1> EQ UD THEN PRINT File is a UNIX directory END ELSE PRINT File type is :DQUOTE(status<1>) PRINT This is not expected for .. END Example 2 Open a file ready to perform file operations in a transaction against it. Make sure the file has not been removed as a transaction type file by a previous invocation of the command jchmod -T CUSTOMERS. INCLUDE JBC.h OPEN CUSTOMERS TO DSCB ELSE STOP 201,CUSTOMERS IF IOCTL(DSCB,JIOCTL_COMMAND_FILESTATUS,status) ELSE CRT IOCTL failed !! ; EXIT(2) END IF status<8,2> THEN CRT Error ! File CUSTOMERS is not CRT part of transaction boundaries !! CRT Use jchmod +T CUSTOMERS !! EXIT(2) END
6-5
IOCTL Function
JIOCTL_COMMAND_FINDRECORD Command
This command will find out if a record exists on a file without the need to actually read in the record. This can provide large performance gains in certain circumstances. Example Before writing out a control record, make sure it doesnt already exist. As the control record is quite large, it will provide performance gains to simply test if the output record already exists, rather than reading it in using the READ statement to see if it exists. INCLUDE JBC.h OPEN outputfile TO DSCB ELSE STOP 201,outputfile ... Make up the output record to write out in output key = output.out rc = IOCTL(DSCB,JIOCTL_COMMAND_FINDRECORD,key) BEGIN CASE CASE rc EQ 0 CRT No further action, record already exists CASE rc GT 0 WRITE output ON DSCB,key PRINT Data written to key : key CASE 1 CRT IOCTL not supported for file type END CASE
6-6
Advanced
Programmers
Reference
JIOCTL_COMMAND_HASH_RECORD Command
For jBASE hashed files such as j1 and j2, each record is pseudo-randomly written to one of the buckets (or groups) of the hashed file. The actual bucket it is written to depends upon two factors: 1. 2. The actual record key (or item-id) The number of buckets in the file (or modulo)
This IOCTL command shows which bucket number the record would be found in, given the input record key. The bucket number is in the range 0 to (b-1) where b is the number of buckets in the file specified when the file was created (probably using CREATE-FILE). The command only returns the expected bucket number, as is no indication that the record actually exists in the file. Two attributes are returned by this command. The first is the hash value that the record key has hashed to, and the second attribute is the bucket number. Example Open a file, and find out what bucket number the record PIPE&SLIPPER would be found in. INCLUDE JBC.h OPEN WEDDING-PRESENTS TO DSCB ELSE STOP key = PIPE&SLIPPER parm = key IF IOCTL(DSCB,JIOCTL_COMMAND_HASH_RECORD,parm) THEN PRINT key :key: would be in bucket :parm<2> END ELSE CRT IOCTL failed, command not supported END
6-7
IOCTL Function
JIOCTL_COMMAND_HASH_LOCK Command
The jEDI locking mechanism for records in jEDI provided database drivers is not strictly a 100% record locking mechanism. Instead, it uses the hashed value of the record key to give a value from 0 to 230-1 to describe the record key. The IOCTL command can be used to determine how a record key would be converted into a hashed value for use by the locking mechanism. Example Lock a record in a file and find out what the lock id of the record key is. The example then calls the jRLA locking demon and the display of locks taken should include the lock taken by this program INCLUDE JBC.h DEFC getpid() OPEN WEDDING-PRESENTS TO DSCB ELSE STOP key = PIPE&SLIPPER parm = key IF IOCTL(DSCB,JIOCTL_COMMAND_HASH_LOCK,parm) ELSE CRT IOCTL failed, command not supported EXIT(2) END PRINT The lock ID for the key is :parm PRINT Our process id is : getpid() READU rec FROM DSCB,key ELSE NULL PRINT Locks taken so far : EXECUTE jRLA -dv
6-8
Advanced
Programmers
Reference
Introduction
This chapter describes how a third party software provider can write a new database driver, and interface it to existing jBASE applications. This is provided through the jEDI API (jBASE External Database Interface). It allows an application that performs its database requests through jEDI (all jBASE applications use jEDI for their database access) to access databases not directly supported by jEDI itself. Note that non-jBASE applications can used jEDI as their database management system, as described in the following chapter jEDI API Calls. A number of standard database drivers built into the jEDI library code are supplied with jBASE, including the j1 and j2 file systems. It is possible to write alternate filing systems conforming to the jEDI API so that all existing applications can use the new database seamlessly, through the new database drivers. The directory $JBCRELEASEDIR/src contains examples of some of the functionality described in this document. The directory $JBCRELEASEDIR/include contains two source files to be included in user-written code, jsystem.h and jedi.h. The source file jsystem.h provides all the necessary definitions for writing external C functions and interfacing them with jBC source files. The source file jedi.h provides all the necessary definitions to allow users to write new database drivers. The following code segment shows an example of a jBASE program accessing two files, and simply copying the records from one file to another: InputFileName = InputFile OutputFileName = OutputFile OPEN InputFileName TO InputDSCB ELSE STOP 201,InputFileName END OPEN OutputFileName TO OutputDSCB ELSE STOP 201,OutputFileName END CLEARFILE OutputFileName SELECT InputDSCB LOOP WHILE READNEXT RecordKey DO CRT RecordKey READ Record FROM InputDSCB,RecordKey ELSE STOP 202,RecordKey END WRITE Record ON OutputDSCB,RecordKey REPEAT This shows typical uses of the database access statements available in jBC, such as OPEN, READ, WRITE, CLEARFILE, SELECT and READNEXT. All of these statements cause, at program execution, a call to the jEDI API. The jEDI API will route the database request to the appropriate database driver for the file type. For example, the input file could be a j1 file while the output file could be a UNIX directory. It is important to note that the application itself is unaware of the database type it is connected to. In some extreme circumstances it may specifically need to know, and can use the IOCTL () function to do so, but this use is rare. The jEDI API has a number of database drivers built in. These include drivers for the various hashed databases, for treating UNIX directories like database files, and so on. For a basic jBASE installation, these drivers are sufficient for all your normal application needs - no knowledge of jEDI, or of setting up jEDI is required. It is possible to write your own database drivers than can be loosely bound to jEDI, so that the OPEN, READ, WRITE etc. calls from an application can be routed to your own code rather than to a supplied database driver.
7-1
If you look at the new source mydd.c, you will see that there is only one exported symbol, namely ExampleInit(). You can change this to be any name you like. For this short tutorial, we will assume the name is left at ExampleInit, as the name has significance later on. Step 2 Compile the database driver and create a shared object out of it. The following steps at shell provide an example of this: % cd $HOME/sosrc ;# Make sure in correct directory % cc -c -I$JBCRELEASEDIR/include mydd.c ;# Compile source to an object % jBuildSLib mydd.o -o $HOME/lib/mydd.so ;# Create shared object out of mydd.o Note that during the building of the shared object stage, the jBuildSLib command will create a shared object at $HOME/lib/mydd.so that contains a single object. You can include other objects on the command line so that mydd.so contains many objects. For example, you could execute: % jBuildSLib mylib.a mydd.o newdd.o -o libfb.so In the above example, the objects mydd.o and newdd.o, plus all the objects in the archive file mylib.a, will be built into a single shared object. Step 3 Create a file definition. There needs to be a UNIX file that tells jBASE This is really a reference to a file contained in a shared object database driver. You can create one using any UNIX mechanism, normally via an editor such as vi. However, the command at shell shown below is equally valid: % echo JBC__SOB ExampleInit .d > $HOME/myfile The above example creates a UNIX file at $HOME/myfile. The first 8 characters are always the same, and are JBC__SOB (note there are two _ characters). The next part, ExampleInit, gives the name of the initialisation function in the database driver, as noted in Step 1. The remainder of the definition can be anything at all - it is up to the database driver to interpret it how it likes. In the supplied database driver, we take the name of the UNIX file opened and append the remainder of the definition to arrive at the name of the file required. This mechanism will become clearer later on. Step 4 Create the database itself. The example given simply uses flat UNIX files in a normal UNIX directory as a crude sort of database. Given the naming convention in Step 3, we can create the database itself with the shell command: % mkdir $HOME/myfile.d
7-2
Step 5 Create some data records. We will create 3 data records of zero length with the following simple UNIX shell commands: % touch $HOME/myfile.d/reca % touch $HOME/myfile.d/recb % touch $HOME/myfile.d/recc Step 6 Either create your own jBC program to access the database in $HOME/myfile.d, or use an existing application. For example, % LIST myfile PAGE 1 myfile........ reca recb recc 3 Records Listed Some assumptions have been made in this example: That the directory $HOME is a component part of the JEDIFILEPATH environment variable, or the JEDIFILEPATH variable is not set. That the directory $HOME/lib is a component part of the JBCOBJECTLIST environment variable or the JBCOBJECTLIST variable is not set. At this stage is it worthwhile understanding the chain of event that occur when accessing the newly created shared object database driver in the above example. The mechanism inside the jBASE run-time library is as follows: The OPEN myfile statement in the jBC source generates a function call to the JLibFOPEN function, as supplied by the jBASE run-time libraries. The JLibFOPEN function generates a call to the generic OPEN function inside the jEDI library. The jEDI OPEN function will attempt to open a UNIX file called myfile in all the directories that are contained in the JEDIFILEPATH environment variable. Eventually the UNIX file $HOME/myfile will be opened. The jEDI OPEN function will now ask all the established database drivers if the file is one that belongs to their type. The database driver for shared objects will understand this as the first 8 characters are JBC__SOB. The shared object database driver will attempt to use code in the jBC run-time library to execute a function taken from shared objects called ExampleInit. The mechanism happens to be the same mechanism that is used for the CALL statement. This will succeed, and the ExampleInit() function will establish itself as a database driver in its own right. The shared object database driver will now pass control to the OPEN function defined when the database driver was established in the call to ExampleInit. Control of the OPEN now passes outside of jEDI and into the user-written OPEN code. The user-written OPEN code now has freedom to do anything it likes. In the example code, it will simply concatenate the strings $HOME/myfile and .d to create a name $HOME/myfile.d and expect this to be an existing UNIX directory. Assuming the user-written OPEN returned a successful code, the OPEN statement now completes. Any future database operations such as READ, WRITE and so on will now be passed to the functions defined by the ExampleInit() function as being responsible for the various operations. The remainder of this topic details all the functions required to create a shared object database driver conforming to the jEDI API. They would normally be in one source code. Only a single function is required to be visible, the remainder can be defined as static. 11:46:55 30 AUG 1994
7-3
The functions can have any name, and the names described in this document are purely examples. The only consideration is that the visible initialisation function name is referenced in the file definition. For example, if you changed the function name ExampleInit to MyInit, the file definition described in Step 3 above would have to reference MyInit instead of ExampleInit. The handle for a jEDI opened file is of type JediFileDescriptor and is defined in the supplied source file $JBCRELEASEDIR/include/jedi.h. Once the file is opened, this handle is passed to the jEDI API and so to the database driver for all future database operations on the file. The jEDI interface does not provide opportunities to create the file nor to delete the file. It is assumed that the database to which you are interfacing will have its own facilities to do this. The functions required to build a database driver conforming to the jEDI API are: 1. Database driver initialisation. This is the only visible function in the database driver source code. The remainder will probably be declared as static functions (not visible outside the source code). It is called when the first file is opened in the program, and is responsible for establishing itself as a database driver. 2. Open file. Performs all necessary functionality to open a file. 3. Close file. Called when a file is closed. 4. Select record keys in a file. The application wants to select some or all of the records in the file. 5. Terminate the selection process. The selection process is terminated. 6. Read the next record from a selection. Following the selection call, the application will repeatedly call this to read the next record key selected. 7. Read a record from the file. Read a single record from the database. 8. Write a record to the file. Write a single record to the database. 9. Delete a record from the file. Delete a single record from the file. 10. Clear all records from the file. Delete all records from the file. 11. Provide locking support. Provide the record locking mechanism to support record locks defined in the application. 12. Provide database specific IOCTL functionality. General functionality mostly unique to the database driver itself, in the same manner as the ioctl() function is fairly device dependent in the C library code. 13. Synchronise the database (flush any cached data). This function is called when the application wants to checkpoint the database and needs to force any data in the cache buffers to disk.
7-4
7-5
Synopsis: static int ReopenAjarFile( FileDescriptor ) JediFileDescriptor * FileDescriptor ; Parameters: FileDescriptor. (Input and Output parameter). This is the file descriptor that was returned when the file was originally opened. Return Value: The return value is 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h. Operation: The address of this function is made available to jEDI during the OPEN function described later. For example, in the OPEN function you might do: FileDescriptor->ProcessReopen = ReopenAjarFile ; Hence, the actual name of the function is not important as jEDI will do the call through a function pointer. This function should re-open the file previously made ajar. The jEDI code will, by default, assume a UNIX file at FileDescriptor->PathName with a UNIX file descriptor at FileDescriptor->FileFd is part of the file operation, and will re-open these automatically.
7-6
command is one of the following to describe the action to take: JEDI_LOCK Take a record lock. If the lock is already taken by another process, then the calling process will wait until it can gain control of the lock.
JEDI_LOCK_NOWAIT Take a record lock, but if the lock is already taken by another process, then return immediately. JEDI_UNLOCK JEDI_UNLOCK_ALL Release a record lock. No operation is performed if the lock doesnt already exist, or it does exist but is taken by another process. Release all the locks taken for this file. The hash parameter is ignored.
7-7
hash is a 32 bit integer that describes the record key to lock or unlock. It is ignored if the command value is JEDI_UNLOCK_ALL. This means it doesnt support full record locking, but a simpler scheme with only an extremely remote chance that hash values will clash for different record keys. The hash value can be anything the database driver likes. The database drive can use the JediBaseHash function (see later) to convert a record key into a 32 bit hash value. The function can return the following values: 0 JEDI_ERRNO_LOCK_TAKEN Any other value indicates that the operation was successful. indicates that the command was JEDI_LOCK_NOWAIT and the requested lock was already taken by another process. indicates a fatal error. The values are usual UNIX error numbers, described in header file errno.h.
7-8
7-9
Once this initialisation code has completed successfully, any database I/O requests for this database driver will be re-directed to the functions declared in the above code.
7-10
7-11
StatInfo. The details returned by function stat() describing the file description originally opened. Status. Set to one or more of the JEDI_STATUS_READ and JEDI_STATUS_WRITE bits to show if the original file description was opened in read only mode, or read and write mode.
The following is a list of members of FileDescriptor that INIT should fill in, assuming the open is successful: ConvertFlags. Some database drivers will perform conversions on the data read in. For example, when reading records from a UNIX directory, the new-line characters will be replaced with attribute marks (i.e. 0x0a characters replaced with 0xfe). If the database driver supports conversion of data, it should initialise this entry with one or more of the JEDI_CONVERT_xxx bits defined in jedi.h. These bits can subsequently be modified by the application by a call to IOCTL. FilesUsed. Enter here the approximate number of system files that will remain open for the file. This allows the ajar processing to try to estimate when files need closing. The value here need not be accurate, it will just be a minor performance hit if wrong. Status. Set additional bits to this field. Note that some may already be initialised by jEDI, so the following bits should be ORed: JEDI_STATUS_FIELDREAD Set if the READ function can perform field read operations. JEDI_STATUS_FIELDWRITE Set if the WRITE function can perform field write operations. JEDI_STATUS_CANBEAJAR Set if this file supports ajar processing. JEDI_STATUS_USING_RLA Set if the file driver is using the jBASE supplied JediSystemLock locking mechanism. See earlier description of the JediSystemLock() function call. JEDI_STATUS_WRAPUP_LOCKS_ONLY When a process terminates, then if this bit is set the jBC run-time code will not call the CLOSE function for this file, but will merely ensure that the locks for this file are released. JEDI_STATUS_READ User is allowed to READ from the file. JEDI_STATUS_WRITE User is allowed to WRITE to the file. TypePtr. This is a void * pointer and allows the database driver to optionally insert any address here of their choosing. This is typically used to store a control block for the file unique to the database driver. FileFlags. Controls operation of the file, and can be one or more of the following bits JEDI_FILE_NOLOG journaler. If set, then updates to the file will not be sent to the jEDI database
JEDI_FILE_NOTRANS If set, then updates to the file will not become part of any transaction. FileType. Any unique integer here, for the use of the database driver only, so long as it doesnt clash with any internally defined values (they have a value less than 256). LockId1. If using the JediSyslockLock() function, then this is the first of two integer required to uniquely identify the file. This is usually set to the expression FileDescriptor->StatInfo.st_dev & 0xffff. LockId2. If using the JediSystemLock() function, then this is the second of two integer required to uniquely identify the file. This is usually set to the expression FileDescriptor->StatInfo.st_ino. ProcessAjar. If the database driver supports ajar processing, then this is the address of the function to call to change the status of a file from open to ajar. This mechanism was described in the earlier section File Ajar Processing. ProcessReopen. If the database driver supports ajar processing, this is the address of the function to call to change the status of a file from ajar to open. This mechanism was described in the earlier section File Ajar Processing.
7-12
7-13
* it to be. The TypePtr member of the * previously allocated structure will * contain a pointer to it. */ if ((BlockPtr->TypePtr=JediMalloc( FileDescriptor, sizeof(struct Select) )) == NULL) { return errno ; } /* * The new structure BlockPtr now needs to * be added to the end of * a linked list of selection structures * allocated against * this file descriptor. */ LoopPtr = &FileDescriptor->SelectPtr; while(*LoopPtr != NULL) { LoopPtr = (struct JediSelectPtr **)(*LoopPtr); } *LoopPtr = BlockPtr ; /* * Set up our NextPtr to be NULL to show we * are at end of linked list. */ BlockPtr->NextPtr = NULL; /* * Return the address of the select * structure allocated to the caller. */ *SelectPtr = BlockPtr ; return 0; In the above code, the first memory allocation (using JediMalloc()) is the standard structure that needs to be allocated. This structure is the one passed to the SelectEnd and Readnext functions (described below). The second memory allocation is optional and is a structure entirely of the making of the database driver. This allows the database driver to have its own data associated with a selection. Note that there can be multiple selections associated with a single file descriptor. For example, a jBC source program could contain the following: OPEN FileName TO FileVar ELSE STOP 201,FileName SELECT FileVar TO SelectList1 ..... SELECT FileVar TO SelectList2 In the above example there will have been two calls to the SELECT function, both calls passing the same file descriptor. Therefore, on the linked list pointed to by FileDescriptor->SelectPtr there will be two select structures. Your SELECT, SELECTEND and CLOSE functions must be able to handle these instances.
7-15
7-16
* the call to SELECT() */ JediFree(FileDescriptor,SelectPtr); /* * The structure passed to us is part of a * linked list of workspaces that * begin at address FileDescriptor->SelectPtr. * The address of our workspace is given * by the variable 'SelectPtr' and the forward * pointer for our linked workspace is * given by the variable 'Next'. * * We now have to go through the list and remove * our workspace from the linked list. */ LoopPtr = &FileDescriptor->SelectPtr; for(;;) { if (*LoopPtr == NULL) break; if (*LoopPtr == SelectPtr) { *LoopPtr = Next ; break; } LoopPtr = (struct JediSelectPtr **)(*LoopPtr); } /* * Finished, so return a good completion code. */ return 0 ;
7-17
struct Sel *OurPtr ; char recordkey[128]; int recordkeylen ; /* * Extract the structure for SELECTs that is unique * to our database driver. */ OurPtr = ((struct Sel *)SelectPtr->TypePtr); /* * We simply return up to 3 record keys, namely * "RecordKey1", "RecordKey2" and "RecordKey3". * Check to make sure that we have not already * returned 3 keys.
jEDI Database Drivers Manual 7-18 Advanced Programmers Reference
*/ if (++OurPtr->currkey > OurPtr->maxkey) { /* * No more valid record keys, so return -1 * in the length field. */ *RecordKeyLenPtr = -1; return 0 ; } /* * Create our actual record key in an * automatic variable */ recordkeylen = sprintf(recordkey, "RecordKey%d",OurPtr->currkey); /* * Make sure the length of the record key * buffer is big enough. */ if (recordkeylen > *RecordKeyLenPtr) { if ((*RecordKeyPtr = JediMalloc( FileDescriptor,recordkeylen)) == NULL) { return errno; } } /* * Return the actual record key and length * of record key. */ memcpy(*RecordKeyPtr,recordkey,recordkeylen); *RecordKeyLenPtr = recordkeylen ; return 0 ;
7-19
RecordKey. (Input parameter). Pointer to a character array describing the record key to read in. The array does not need to be 0 terminated. RecordKeyLen. (Input parameter). The length of the RecordKey parameter. BufferPtr. (Input and Output parameter). This is the address of a character array where we will place the record data. The length of this is given by the BufferLenPtr parameter. Should this buffer not be large enough to accommodate the record, we will allocate more space using JediReadMalloc() and return here the address of the data space allocated. Thus the calling function can determine where the record was placed, and thus whether it was placed in the character array supplied to READ, or the character array allocated by READ. It is up to the calling function to free any allocated space using JediFree() if necessary. BufferLenPtr. (Input and Output parameter). This is the address of an integer, originally set up to describe the size of the character array pointed to by * BufferPtr. Upon return from READ, this will indicate the size of the record that was read in. FieldNumber. (Input parameter). If the caller wishes to read an individual field instead of the entire record, then they will set the JEDI_RECORD_FIELDREADWRITE bit in the Flags parameter and set this parameter to be the field number to read in. If the database driver does not support this operation, you can ignore this field (see earlier description of JEDI_RECORD_FIELDREADWRITE). Return Value: 0 is returned if the record is read in successfully. ENOENT is returned if the record does not exist. Any other value shows the reason the operation failed using the values defined in errno.h. Note: Although the database driver is only responsible for the above return values, the jEDI code may return other codes to the calling application outside the control of the database driver. These codes are EDEADLK (when a deadly embrace has been detected and avoided), and JEDI_ERRNO_LOCK_TAKEN (when a lock with NO WAIT was specified, and the lock was already taken by another process).
7-20
Operation: The READ function should, by default, read in an entire record. If the bit defined by (Flags & JEDI_RECORD_FIELDREADWRITE) is set, then the database driver should just return a single field from the record, as given by parameter FieldNumber. Note that if database driver doesnt want to support reading of individual fields, then it should not set the JEDI_STATUS_FIELDREAD bit in the Status field of the file descriptor during the OPEN function call, leaving it up to the application to extract the individual field from the entire record. It will return the data at the address *BufferPtr and return the length of the record at *BufferLenPtr. The database accessed by the database driver will probably have an entirely different record structure to that expected by the application. Therefore it is up to the database driver to convert the record from the foreign database into a format understandable by the calling application. For example, if the database had defined the record layout as: INT STRING FLOAT UserId Name(20) Balance 4 byte integer field. 20 character name field. 8 byte monetary value field.
Then it would be up to the database driver to convert it to a format acceptable to the calling application. Using the above example, the following code would perform the conversion: char char int record[32]; output[128]; outputlen ;
/* * Create the first field as a string followed * by a field delimiter, taken from a 4 * byte integer. */ outputlen = sprintf(output, %d\376,*((int*)&record[0])); /* * Now add the second field to the output record. * This is a 20 character field. */ memcpy(&output[outputlen],&record[4],20); /* * Finally add the field delimiter for the * second field, and create the third field taken * from a floating point number. */ outputlen += (20 + sprintf( &output[outputlen+20], \376%f,*((float*)&record[24])); This conversion process can specifically be bypassed by the calling application if required. This may be needed from time to time for certain applications to read in the data in binary format without any conversions. The read record function should check the integer at FileDescriptor->ConvertFlags to see what sort of conversion should take place (see the OPEN and IOCTL functions). The example below shows a simple read record process that simply returns 4 fields in a record, each field delimited by 0xfe (or 0376 in octal). No account of any record locks is made, no account of reading single fields is allowed for and no account of any conversions is used. char temprecord[4096]; int temprecordlen ; int linecount ; /* * Create the record, delimited by 0xfe * (or 376 in octal), in a temporary automatic * variable. This record will just be the name of * the record key copied 4 times. This means * we will have 3 field delimiters. */ for (temprecordlen = linecount = 0 ;
7-21
linecount < 4 ; linecount++) { memcpy(&temprecord[temprecordlen], RecordKey,RecordKeyLen); if (linecount == 3) { temprecordlen += RecordKeyLen ; } else { temprecord[temprecordlen+RecordKeyLen] = 0376 ; temprecordlen += (RecordKeyLen +1) ; } } /* * Make sure the caller has provided enough space * for the data to be copied to. If not, then * allocate more space. */ if (temprecordlen > (*BufferLenPtr)) { if (((*BufferPtr) = JediReadMalloc( FileDescriptor,temprecordlen)) == NULL) { return errno; } } /* * Copy the data to the users buffer and return to * the caller the length of the record returned. */ memcpy(*BufferPtr,temprecord,temprecordlen); *BufferLenPtr = temprecordlen ; return 0 ; Note that when the buffer was allocated, instead of using JediMalloc() we actually used JediReadMalloc(). This is a special case for performance reasons and the database driver should use a call to JediReadMalloc() only when it is creating a buffer for data to be read into. There are no other occasions when you use JediReadMalloc(). The application can make use of this by trapping calls to JediReadMalloc(), as it knows that during these calls the database driver is creating a data space to return the record. Thus, the application can make sure the data is read into the final resting place for the data, rather than into some temporary buffer allocated created by JediMalloc(), that then needs copying elsewhere. There is no locking flags passed to the READ function. The is because the application request to read a record with a lock goes through some base code in jEDI, and before the request reaches the database driver, jEDI will make a call to the LOCK function (detailed later) to do any record locking. Hence, there is no requirement for the READ functionality in the database driver to explicitly worry about locks. The database driver may support the conversion of data, for example when reading in UNIX files it may convert new-line characters to attribute marks (from 0x0a to 0xfe). If this is supported by the database driver, the ConvertFlags member in the FileDescriptor structure passed to this function will show what sort of conversion, if any, to perform. This ConvertFlags member is a number of bits defined by JEDI_CONVERT_xxx in the header file jedi.h, and is initialised during the OPEN call. Subsequent calls by the application to IOCTL can modify this field. It is up to the database driver to decide if to support this functionality or not.
7-22
RecordKey. (Input parameter). This points to a array of characters that define the record key for which to write the record as. The array is not a 0 terminated string. RecordKeyLen. (Input parameter). Shows the length of the record key as pointed to by the parameter RecordKey. BufferPtr. (Input parameter). This points to the actual data to write out, and is of length BufferLen bytes. BufferLen. (Input parameter). This shows the amount of data to be written, as pointed to by the BufferPtr parameter. FieldNumber. (Input parameter). If the caller wishes to write an individual field instead of the entire record, then they will set the JEDI_RECORD_FIELDREADWRITE bit in the Flags parameter and set this parameter to be the field number to write out. If the database driver does not support this operation, you can ignore this field (see earlier description of JEDI_RECORD_FIELDREADWRITE). Return Value: The return value is 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h. Operation: The WRITE function should, by default, write out an entire record. If the bit defined by (Flags & JEDI_RECORD_FIELDREADWRITE) is set, then the database driver should just write a single field, as given by parameter FieldNumber. Note that if database driver doesnt want to support writing of individual fields, then it should not set the JEDI_STATUS_FIELDWRITE bit in the Status field of the file descriptor during the OPEN function call, leaving it up to the application to insert the individual field and write the entire record. There is no locking flags passed to the WRITE function. The is because the application request to write a record and maintain or release a lock goes through some base code in jEDI, and before the request reaches the database driver, jEDI will make a call to the LOCK function (detailed later) to do any record locking releases. Hence, there is no requirement for the WRITE functionality in the database driver to explicitly worry about locks.
7-23
The foreign database accessed by the database driver will probably have an entirely different record structure to that expected by the application. Therefore it is up to the database driver to convert the record from the format by the application to the format of the foreign database. This is the reverse process as detailed in the READ record function described previously. The format of the record that is pointed to by the BufferPtr parameter is that whereby all the fields in the record are string fields delimited by a 0xfe (or octal 0376) characters. Like the READ record function, the database driver has to convert the fields as necessary. This conversion process can specifically be bypassed by the calling application if required. This may be needed from time to time for certain applications to write out the data in binary format without any conversions. The write record function should check the integer at FileDescriptor->ConvertFlags to see what sort of conversion should take place (see the OPEN and IOCTL functions). The example below shows a simple write record process that simply writes the record out to a UNIX flat file. No account of writing single fields is allowed for and no account of any conversions is used. char ActualPathName[PATH_MAX+1]; int i1, fd1 ; /* * Make up the name of a UNIX file to write to. * This is basically the concatenation of the file * name that was opened plus the record key. */ i1 = strlen(FileDescriptor->PathName); memcpy(ActualPathName,FileDescriptor->PathName,i1); ActualPathName[i1] = '/'; memcpy(&ActualPathName[i1+1],RecordKey, RecordKeyLen); ActualPathName[i1+1+RecordKeyLen] = NULL; /* * Now to try to open the file. */ if ((fd1=open(ActualPathName, O_WRONLY|O_TRUNC|O_CREAT,0666)) < 0) { return errno; } /* * Now to write the data to the actual file. */ if (write(fd1,BufferPtr,BufferLen) != BufferLen) { close(fd1); return (errno == 0 ? EIO : errno); } /* * Write succeeded. Close the file and return 0. */ close(fd1); return 0 ; The database driver may support the conversion of data so that for example, it will convert new-line characters to attribute marks when reading in UNIX files. If this is supported by the database driver, the ConvertFlags member in the FileDescriptor structure passed to this function will show the type of conversion, if any, to perform. This ConvertFlags member is a number of bits defined by JEDI_CONVERT_xxx in the header file jedi.h, and is initialised during the OPEN call. Subsequent calls by the application to IOCTL can modify this field. It is up to the database driver to decide if to support this functionality or not.
7-24
7-25
if ((DirPtr = opendir(FileDescriptor->PathName)) == NULL) { return errno; } while ((Next = readdir(DirPtr)) != NULL) { if ((ReturnValue=JediBaseSignalCheck()) != 0) { break; } strcpy(PathName,FileDescriptor->PathName); strcat(PathName,"/"); strcat(PathName,Next->d_name); /* * Find out the type of the entry. We will not * attempt to delete anything other than * regular files. */ if (stat(PathName, &stat_info) == 0) { if (stat_info.st_mode & S_IFREG) { unlink(PathName); } } } closedir(DirPtr); return 0;
7-26
Note the function call to JediBaseSignalCheck(). This function will return a non-zero value should the user decide to interrupt and abort the clear file operation. This function is documented earlier in the chapter.
7-27
7-28
unsigned char c1, c2 ; /* * First of all, create a HASH value for * the RecordKey. */ if (RecordKey != NULL) { HashValue = JediBaseHash(RecordKey, RecordKeyLen, 1); /* * Mask off the top bit to make it a signed * positive value. Due to bug in a previous * compiler, we mask the top two bits. */ HashValue &= 0x3fffffff; } /* * Secondly, see if we are to remove locks. */ if (Flags & JEDI_RECORD_UNLOCKRECORD) { if (RecordKey == NULL) { /* * Release ALL locks on the file. */ return JediSystemLock(FileDescriptor, JEDI_UNLOCK_ALL, 0) ; } /* * Release a single lock on the file. */ return JediSystemLock(FileDescriptor, JEDI_UNLOCK, HashValue ) ; } /* * Thirdly, we must be setting a lock. Do we have * to wait or not ? */ return JediSystemLock(FileDescriptor, (Flags & JEDI_RECORD_LOCKRECORD_NOWAIT ? JEDI_LOCK_NOWAIT : JEDI_LOCK ), HashValue ) ;
7-29
ReturnValue = 0; switch(SubCommand)
7-30
{ case
JEDI_IOCTL_CONVERT: /* * The application wants to change the status * of the conversion flags, which are usually * examined to see if to read a record in * binary format without conversion, or to do * the conversion between fields on the foreign * database and the format expected by the * application. * The input string will be something like * RB,WB, which means all the reads should * be binary and the writes should be binary. */ if (IoctlReturnAddr != NULL && IoctlAddr != NULL) { FileDescriptor->ConvertFlags = JediBaseIoctlConvert( FileDescriptor->ConvertFlags, IoctlAddr, IoctlLen, IoctlReturnAddr, IoctlReturnLen) ; } break; case JEDI_IOCTL_FILESTATUS: /* ** The application wants details of the file. * * Return details as an attribute delimited * record as follows : * <1> File Type as a string * <2> FileFlags, as decimal number, * showing the LOG, BACKUP and * TRANSACTION permissions. * <3> BucketQty, as decimal number, * number of buckets in the file. * Not applicable in this example. * <4> BucketSize, as decimal number, size * of each bucket in bytes. * Not applicable. * <5> SecSize, as decimal number, size of * secondary data space. * Not applicable in this example. * <6> Restore Spec, a string showing any * restore re-size specification. * Not applicable in this example. * <7> Locking identifiers, delimited by * multi-values. * <8> FileFlags showing LOG, BACKUP and * TRANSACTION permissions. Multi-values * are : * <8,1> set to 1 if no logging. * <8,2> set to 1 if no transaction. * <8,3> set to 1 if no file backup. */ ReturnBufferLen = sprintf(ReturnBuffer, "Example\3760\376\376\376\376\376%d \375%d\3760\3750\3750", FileDescriptor-LockId1, FileDescriptor->LockId2); if (IoctlReturnAddr != NULL) { i1 = min(ReturnBufferLen,*IoctlReturnLen); memcpy(IoctlReturnAddr,ReturnBuffer,i1); *IoctlReturnLen = i1 ;
7-31
} else { ReturnValue = EINVAL; } break; case JEDI_IOCTL_HASH_LOCK: /* * Find the HASH value used in any locking * scheme. */ if (IoctlReturnAddr != NULL && IoctlAddr != NULL) { i1 = JediBaseHash(IoctlAddr, IoctlLen,1); /* * Mask off the top bit to make it a * signed positive value. * Due to bug in a previous compiler, * we mask the top two bits. */ i1 &= 0x3fffffff; *IoctlReturnLen = sprintf(IoctlReturnAddr,"%d",i1); } break; case JEDI_IOCTL_FINDRECORD: /* * Test whether record exists. * Work out a full path name to use. */ i1 = strlen(FileDescriptor->PathName); memcpy(ActualPathName, FileDescriptor->PathName,i1); ActualPathName[i1] = '/'; memcpy(&ActualPathName[i1+1],I IoctlAddr,IoctlLen); ActualPathName[i1+1+Ioctl] = NULL; if ((fd1=open(ActualPathName,O_RDONLY)) < 0) { ReturnValue = errno; } close(fd1); if ( IoctlReturnAddr != NULL ) { i1 = min(IoctlLen,*IoctlReturnLen); memcpy(IoctlReturnAddr,IoctlAddr,i1); *IoctlReturnLen = i1 ; } break; default: ReturnValue = -1; break; } return ReturnValue ;
7-32
7-33
Introduction
The previous chapter concentrated on creating new database drivers, enabling existing jBC or C programs to access alternative databases. The most common uses of jEDI will be through statements such as READ, WRITE, OPEN etc.. within a jBC application. jEDI is designed to be a completely autonomous component of jBASE, and while it is used extensively by a jBC program, it is very capable of being used by a C program that has no jBC code anywhere in sight. The purpose of this section is to describe how to write a C program that calls the jEDI API. Many of the tools provided with jBASE use jEDI in this manner. An example source of a C program calling jEDI directly is provided with jBASE in the file $JBCRELEASEDIR/src/jediCExample.c. This program performs a simple file copy from one jEDI supported file to another jEDI supported files, and shows the use of many of the functionality described later. Provided with jBASE are a number of #include files you will need in your program. These files are jsystem.h and jedi.h and are found in directory $JBCRELEASEDIR/include. All the non-standard definitions described in the section, such as JEDI_TRANSLOG_COMMAND_QUERY are defined in one of these two files, so you should #include both of them. Note that jsystem.h also does a #include <stdio.h>. The stages of calling jEDI directly from a C program can be summarised as: Initialisation. Making jEDI database requests. Program termination.
The operations of transaction boundaries and transaction journaling are done automatically by jEDI and no intervention on the part of the C program is required - with the exception of abnormal program termination, which is discussed later.
8-1
Initialisation
There are two operations to perform before making the first database operation, both of them are optional. Set up signal handlers. You should ideally set up signal handlers such that in the event of an unexpected program termination, you can still perform the wrap-up operations detailed in Program Termination later. Basically, at program termination you will abort any transaction and close all opened files (thus releasing all the locks). Most database drivers, including the ones supplied with jBASE, can cope with abnormal exits that do not wrap-up properly. Thus, while setting up signal handlers and wrapping up cleanly is the preferred option, it is not mandatory. Connect to the database. This involves calling the function JediConnect to return a jEDI connection handle. This connection handle is then passed to all subsequent OPEN requests. If this stage is omitted, you can always use the default connect handle of 0. However, the connection function provides defines for the storage allocation functions to call (malloc, strdup, etc.) and as we shall see later, this may provide performance improvements in your code. A typical call to JediConnect might be: static void * readalloc() ; int ConnectHandle ; struct JediConnectBlock ConnectBlock ; /* * Set up the database name to connect to. * This is reserved at present, so use string. */ ConnectBlock.dbname = "" ; /* * Set up the addresses of all the memory * allocation functions we shall require. * Note the readmallocptr member, which is to * our own allocation function called readalloc * As we see later, this will provide performance * improvements. */ ConnectBlock.mallocptr = (void *(*)())malloc ; ConnectBlock.readmallocptr=(void *(*)())readalloc; ConnectBlock.reallocptr = (void *(*)())realloc ; ConnectBlock.freeptr = free ; ConnectBlock.strdupptr = (char *(*)())strdup ; /* * Connect to jedi and check return code. */ if ((ConnectHandle=JediConnect(&ConnectBlock))<0) { perror("JediConnect"); exit(1); } /* * We use the variable ConnectHandle in all * subsequent calls to jedi. */
8-2
JediOpenDeferred Open a file in deferred mode. The file is only partially opened, and will be genuinely opened on the first database access using the returned file descriptor. JediClose JediSelect JediSelectEnd JediReadnext JediReadRecord JediWriteRecord JediDelete JediLock JediIOCTL JediSync JediClearFile JediPerror JediFileOp JediReinitialise Close a file. Initiate a selection of a list of record keys. Terminate the selection of the list of record keys. Read the next record key following a selection. Read a record (or a field from a record) from the file. Write a record (or a field within a record) to a file. Delete an entire record. Perform record locking. General purpose file control. Flush updates to the journaler and to disk. Clear all records from the file. Similar to the C library function perror, except it has allowances for the extensions to the range of errno values that can be returned. Provides general mechanism to create, delete and clear files that are supported as a resident jEDI database type. Allows a certain amount of re-initialisation of jEDI under certain circumstances.
8-3
Program Termination
There are basically two things to do when a program terminates, whether it terminates normally or because of a signal. You should first abort any outstanding transactions (assuming you have used transaction support), and secondly close any opened files. Aborting outstanding transactions. If you are using transaction boundaries in your program, you should check to see if you have an open transaction, and handle appropriately to your application. The following code shows a check to see if a transaction was open. If it was, the transaction is aborted and an error message is printed. if (JediTransLog(JEDI_TRANSLOG_COMMAND_QUERY)) { JediTransLog(JEDI_TRANSLOG_COMMAND_ABORT, 0, Abort from program xxx ); fprintf(stderr,Forced transaction abort\n); } Close opened files. You should ideally keep a list of all currently opened files so that you can close them at a later stage. The closing of files will also release all the locks on the file.
8-4
Note the use of the -s option to strip the symbol table, debugging and line number information from the executable. These are not needed and will significantly reduce the size of the executable program. The command is simplified if you use the jbc command to build your application,. There are basically two ways of building your application. The first is the normal method, using jEDI shared libraries. The second uses jEDI archive libraries. For example, to link the object to create an executable program using the jbc command and shared libraries: % jbc myprog.o -o myprog To link with archive libraries, the command would become: % jbc myprog.o -o myprog -JLa When linked with jEDI shared libraries, you must ensure the environment variable LD_LIBRARY_PATH shows where it can find these shared libraries (use LIBPATH for AIX systems). For example, you could do this in your .profile: export LD_LIBRARY_PATH=$JBCRELEASEDIR/lib
8-5
Starting a Transaction
if (JediTransLog(JEDI_TRANSLOG_COMMAND_START, Flags, message) != 0) { perror(TRANSTART); } Where: JEDI_TRANSLOG_COMMAND_START Tells the function to start a transaction boundary.
Flags If the JEDI_TRANSLOG_FLAGS_SYNC bit is set, following the termination of the transaction though an abort or commit, the updates will be flushed to disk and if the transaction journaling is operative, the update information flushed to the output media. This gives greater database integrity in the event of a failure at the cost of an additional overhead. If no synchronisation is required, pass 0 in this parameter. message Can be any 0 (null) terminated string of characters, and is used simply by the transaction journaler to record information about the transaction. This string should be meaningful - you might want to examine the updates later though the transaction journaling mechanism. The return value is 0 if successful, and any other value if there is an error.
message Can be any 0 (null) terminated string of characters, and is used simply by the transaction journaler to record information about the transaction. This string should be meaningful - you might want to examine the updates later though the transaction journaling mechanism. The return value is 0 if successful, and any other value if there is an error.
8-6
message Can be any 0 terminated string of characters, and is used simply by the transaction journaler to record information about the transaction. This string should be meaningful - you might want to examine the updates later though the transaction journaling mechanism. The return value is 0 if successful, and any other value if there is an error.
8-7
8-8
8-9
8-10
8-11
8-12
8-13
8-14
RecordKey. Pointer to a character array describing the record key to read in. The array does not need to be 0 terminated. RecordKeyLen. The length of the RecordKey parameter. BufferPtr. This is the address of a character array where we will place the record data. The length of this is given by the BufferLenPtr parameter. Should this buffer not be large enough to accommodate the record, we will allocate more space and return here the address of the data space allocated. Thus the calling function can determine where the record was placed, and whether it was placed in the character array supplied to READ, or the character array allocated by READ. It is up to the calling function to free any allocated space if necessary. BufferLenPtr. This is the address of an integer, originally set up to describe the size of the character array pointed to by * BufferPtr. Upon return from READ, this will indicate the size of the record that was read in. FieldNumber. If the caller wishes to read an individual field instead of the entire record, they will set the JEDI_RECORD_FIELDREADWRITE bit in the Flags parameter and set this parameter to the field number to read in. If the database driver does not support this operation, you can ignore this field (see earlier description of JEDI_RECORD_FIELDREADWRITE). Return Value: 0 is returned if the record is read in successfully. ENOENT is returned if the record does not exist. Any other value shows the reason the operation failed using the values defined in errno.h.
8-15
RecordKey. This points to a array of characters that define the record key for which to write the record as. The array is not a 0 (null) terminated string. RecordKeyLen. Shows the length of the record key as pointed to by the parameter RecordKey. BufferPtr. This points to the actual data to write out, and is of length BufferLen bytes. BufferLen. This shows the amount of data to be written, as pointed to by the BufferPtr parameter. FieldNumber. If the caller wishes to write an individual field instead of the entire record, they will set the JEDI_RECORD_FIELDREADWRITE bit in the Flags parameter and set this parameter to the field number to write out. If the database driver does not support this operation, this will be ignored. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h.
8-16
8-17
8-18
8-19
8-20
8-21
8-22
filespec. This string defines the specification of the file, such as "DICT Filename 1,1 TYPE=HASH1" unixoptions. The options that would normally be specified on the command line in the Unix manner. This is a string with one or more of the following characters: B L T H File not Backed up using jbackup. File not Logged to the logger. File not part of Transaction boundaries. Do not fill holes when creating files.
legacyoptions. The options that would normally be specified on the command line in the manner of legacy applications. This is a string with the same characters and meaning as for the unixoptions parameter. errormessage. Address if a 1024 byte buffer where an error message string should be stored if an error occurs. The error strings define record keys in the jBASE error message file (usually $JBCRELEASEDIR/jbcmessages). You can find the error messages by typing the following command from a UNIX shell : LIST $JBCRELEASEDIR/jbcmessage EQ \"JEDI_FILEOP]\" Note that the string returned here is actually a number of fields delimited by value marks, 0xFC. The first field is the actual error message key, and the remaining fields are the operands to the message key - such as the name of the file.
8-23
interrupt. The address of a function to call to establish all your signal handlers. When you call JediFileOp, it has to set up some of its own signal handlers to handle the SIGINT and SIGQUIT signals. By passing the address of a function, you can cause it to re-establish your signal handlers upon completion. You can also pass -1 here to ignore this functionality. Return Value: 0 if the operation succeeded. Any other value shows that the operation failed. The errormessage buffer is updated with an appropriate error message - held in the jBASE error message file, with appended operands delimited by value marks, 0xFC. Example: We wish to create a file called 'SLIPPER', but only the DICTionary part of it. The file will be a j2 type (denoted by HASH2), will have 17 buckets, and we do not want the file backed up using the jbackup command. #include <jsystem.h> #include <jedi.h> main() { char buffer[1024] ; int returncode ; if ((returncode = JediFileOp ( JEDI_FILEOP_CREATE_FILE , JEDI_FILEOP_DICT_ONLY , "SLIPPER 17,1 TYPE=HASH2" , "B" , "" , buffer , (void (*)())-1)) != 0) { fprintf(stderr,"Cannot create file SLIPPER\n"); } exit (returncode) ; }
8-24
8-25
Chapter 9: Makefiles
Introduction
The standard UNIX development facility called 'make' is designed to help programmers manage many source files, and to build applications from the source files automatically. In its simplest form, make keeps track of changes in source files and when asked to, it will recompile all the parts of the program that have been affected by changes. When invoked, the make command looks for a file called Makefile (or makefile) in the current directory. It expects this file to describe the rules for building your application. An example of a small makefile is given later. The make facility is fully documented in your UNIX development documentation. The remainder of this chapter will show you how an example jBASE application could use the make facility, but it will not attempt to document make itself. Remember that if you do not feel comfortable with using make files, you can carry on using the BASIC and CATALOG commands that you are used to. However, most application developers find than once they have gone through the pain of creating their make files, it is well worth the initial effort. Make files work best when the sources have a defined extension to their file name. jBC source code does not have any particular naming convention when using BASIC or CATALOG, but when you use the jbc command or make files the source code files should have a .b extension. As an example, assume we have two programs, 2 subroutines and 2 C functions. These are in source file names prog1.b, prog2.b, sub1.b, sub2.b, func1.c and func2.c respectively. Assume also that they are all in the same directory and there is a make description file in the directory called Makefile. This is what the Makefile might look like: # # This is the example make file for the sources # prog1.b, prog2.b , sub1.b , sub2.b , func1.c and # func2.c. # # Declare the fact some of the suffixes are .b # .SUFFIXES: .b # # Declare the options we will use to the jbc compiler. # OPTIONS = -JO2 # # Declare the name of the library we keep the C object # and the SUBROUTINE object in. # LIBCFUNCS = libFUNC.a LIBSUBS = libSUBS.a # # Declare the name of the shared object we will build # when we have built the subroutines into archive # libraries. # LIBSUBSOUTPUT = libSUBS.so # # Declare the objects that will go in the archive # libraries # LIBCOBJS = $(LIBCFUNCS)(func1.o) \ $(LIBCFUNCS)(func2.o) LIBSOBJS = $(LIBSUBS)(sub1.o) \ $(LIBSUBS)(sub2.o) # # Declare the programs to build. # PROGRAMS = prog1 prog2 # # Stop the make utility from deleting these libraries
9-1
Makefiles
# if you interrupt the make. # .PRECIOUS: $(LIBCFUNCS) $(LIBSUBS) # # Declare the programs to execute when we need to # re-compile a C source or jbc source, and put it in # the library. # .c.a: cc -c $< -I$(JBCRELEASEDIR)/include ar -rv $@ $(?:.c=.o) .b.a: jbc -Ja$@ $< # # The first target name we defined below is the target # name that make will try to build if you just enter # 'make' on the command line. # all: $(LIBCFUNCS) $(LIBSUBS) $(PROGRAMS) # # We can now define another target name called 'clean' # that will update the time stamp for all the sources # and then call 'make all' to re-build all the # programs regardless. # clean: touch *.[bc] -rm *.[oa] make all # # Create a target name called 'release'. This copies # all the programs from the current directory to the # directory where they will actually be run from, and # copies the shared objects containing the SUBROUTINES # to the target directory. # release: cp $(PROGRAMS) $(HOME)/bin cp $(LIBSUBSOUTPUT) $(LIBSUBSOUTPUT).el $(HOME)/lib # # Describe how to build the libraries. # The libraries are built initially using the rules # '.c.a' and '.b.a' that were defined earlier. For the # SUBROUTINE library, we additionally call jBuildSLib # to create a shared object from the archive library. # $(LIBCFUNCS): $(LIBCOBJS) $(LIBSUBS): $(LIBSOBJS)
jBuildSLib $(LIBSUBS) $(LIBCFUNCS) -o $(LIBSUBSOUTPUT)
# # Describe the rules for building the programs. Note # that we describe the C functions as dependencies, so # they will get built first. # $(PROGRAMS): $$(@).b $(LIBCFUNCS) jbc $@.b -o $@ $(LIBCFUNCS) The make file has a number of target names to allow you to develop the example application using the following steps: 1. Set your environment to use local copies of the programs and subroutines using the following commands: % export PATH=`pwd`:$PATH % export JBCOBJECTLIST=`pwd`:$JBCOBJECTLIST
Makefiles Manual
9-2
2. Edit the sources prog1.b, prog2.b , sub1.b, sub2.b, func1.c, func2.c 3. Create the file 'Makefile' as shown above. 4. Build the programs using the following command : % make or % make all 5. If there are any compilation errors, edit the source and re-build the application using 'make' or 'make all'. 6. Test the application. If there are any application errors, edit the source and re-build using 'make' or 'make all'. The make command will only re-build the sources that have been affected by changes. 7. Once you have the application working, you may like to re-compile ALL the sources again just to be 100% sure, using the following command : % make clean 8. In the first step you modified JBCOBJECTLIST and PATH to give the current directory precedence so, up to this point, these program and subroutines will all have been built in the current directory, and only you will have access to them. The following command will copy the executable programs and shared object subroutines into the directory used by other users in the account. Do this when you have finished testing your application and want to make it generally available: % make release Although not intended as a make tutorial, you may find the following points helpful when you are looking at the above example: The make command contains a large number of command line options, internal rules, default rules, macros and so on. These are all explained by executing 'man make' from your shell command line. They are worth looking at in some detail. When you execute the make command you give it a target name to describe what to actually make. Your make file can have one or more target names. If you omit the target name on the command line, make will use the first target name it finds. In the above example, the target names are 'all', 'clean' and 'release'. So, from the command line, any of the following are valid : % make % make all % make release # Uses the 'all' target as default # Uses target name 'all' , currently default # Uses the target name 'release'.
% make all release # Builds all programs then releases them. Notice how the 'clean' target name actually calls the make utility recursively after it has forced a re-build of all the objects. By default, should any of the executed programs return a completion code other than 0, make will abort the build at this point and return a completion code other than 0. This can be turned off by using the -i option when invoking make, for example: % make -i release Note you can also stop the checking of the return code of individual commands in the make file by preceding it with - , as we have used in the example '-rm *.[oa]'. This is useful if you are unconcerned if the command fails. In our example the rm may fail if the files to delete do not exist already, and so we are not concerned with this error. The example shows the use of jbc and jBuildSLib instead of BASIC and CATALOG. You can amend the make files to use BASIC and CATALOG, but this will make the files more complicated. There are a number of macros used in the make file, such as $@, @<, substitution of object names. Use 'man make' for more information. @(?:.c=.o). These perform general
9-3
Makefiles
The make files support the use of 'include' to include other make files. This is synonymous with the use of INCLUDE or #include in jBC and C source code. For example, instead of using the command jBuildSLib, you could define the command in a common make file, and use the definition in another make file. If we assume the file at $HOME/makecommon has the following line in it : BUILDCMD = jBuildSLib in your own make file you could do the following : include $(JBASEHOME)/makecommon $(BUILDCMD) $(LIBSUBS) $(LIBCFUNCS) -o $(LIBSUBSOUTPUT)
Makefiles Manual
9-4
Introduction
There are two main mechanisms for compiling a source to an object. The jbc command provides all the functionality to compile source to an object, and optionally to create executable programs or archive libraries from the object. It has many options to control the compilation and linking. It only works on UNIX files, so any source files in jBASE hashed files, for example, cannot be used with the jbc command. This is not normally a problem, as developers tend to keep their source code in UNIX directories and they can use UNIX tools on the sources such as make, grep, vi, ar, cc and so on. The BASIC command is a simple-to-use command that is really just a front-end program to the jbc command. It has fewer options and less flexibility than the jbc command, but for a substantial number of developers it provides all the functionality they need. The BASIC command works with any files supported by jEDI, such as UNIX directories, hashed files and so on. The output of the BASIC command is the creation of a compiled object that is placed in the same file as the source but with a slightly different name.
10-1
jbc Command
The jBC compiler consists of a sophisticated set of compilation tools used to compile and link jBC, C, assembler, libraries and object code to produce UNIX executable files. It provides new UNIX tools to process jBC files and uses the same tools as the UNIX C compiler for processing C code. The compilation tools consist of a jBC pre-processor (jpp), a jBC cross compiler (jbccom), jBC optimiser (jbcoptim), a C pre-processor (cpp), C compiler (ccom), assembler optimiser (coptim), assembler (as), and linker (ld). The compiler is driven through the jbc interface command. The interface accepts many options that control the relevant tools in the compilation process. It also provides transparent access to the tools mentioned previously. The jbc command accepts several different file types as source file arguments and performs the required processing on each. The order of processing is shown in the figure on the next page. As illustrated, the jbc command can process jBC source programs, C source programs, UNIX assembler source, libraries and object programs to produce a UNIX executable file. The compilation process can be halted at any of the stages shown by providing the appropriate options to jbc. Compilation of the intermediate files can subsequently be completed, again using the jbc command. jBC source code is compiled into machine code and, as there is no CPU overhead involved with interpreting pseudo object code, the resulting code executes extremely quickly.
10-2
The default operation of jbc is to compile a number of sources and create an executable file named a.out. Optionally, the user may halt the compilation process at any stage and examine the resultant code. As the compiler also makes use of the UNIX compiler, pre-processor and link-loader, the options for these utilities are also available to the jBC compiler. Some common options are described later. jbc command syntax is: jbc {-option {...-option}} file {{file}...} file This is a list of programs as arguments, separated by spaces. The source provided can be jBC, C, assembler or an object program, and is processed accordingly.
-option This is a character string representing one or more jbc options. Options passed to the command must be preceded by a hyphen, or they will be interpreted as a file which jbc will try (and fail) to compile. The options directly supported by the jbc command mostly begin with -J. The other options supported are really for the UNIX cc command. The options available to jbc can be split broadly into five categories: 1. cc only. These options are not used by jbc and are passed directly to the cc command when the cc command is called. 2. Common cc and jbc options. These options are used by both the cc command and the jbc command. 3. jbc only standard options. Normal options used purely by the jbc program. 4. jbc SQL options. These options are only used when compiling sources with embedded SQL statements, which is an optional feature of jBASE. 5. jbc C++ options. These options are used to create C++ code instead of C code from a jBC source.
-Ldirname -S
10-3
creation of the .c and .j files. This way, the user can examine the C code generated by the jbccom compiler. This C source can then be used later as an input file to jbc (or cc) if required. -Yb,dirname Shows the directory for the jbccom compiler. By default, we use $JBCRELEASEDIR/bin/jbccom, but use this option for any other directory.
-Jm
-Jo
-Js -Jv
Option -JLa
Description Select archive libraries. Normally a jBC program is linked using shared object libraries. This makes the link phase much quicker, as well as making the executable much smaller. There may be times when you want to link with jBC archive libraries though,
10-4 Advanced Programmers Reference
such as creating an executable with the set-userid bit set, or to be independent of the release of jBASE. -JLS Static linking of subroutines. Under normal circumstances the CALL statement generates code such that the subroutine it is calling is dynamically linked at run time. You can use the -JLS option so that the subroutine is linked at compile time. This will marginally speed up the time for the CALL to execute. Optimisation level 1. No code optimisations are performed and debug information is produced for both the C debugger and the jBC debugger. Optimisation level 2 (default). Debug information is produced for the jBC debugger but not for the C debugger. Minimal optimisation occurs. Optimisation level 3. Debug information is produced for the jBC debugger. Optimisation of jBC occurs and matrix boundary checking is removed. The C code is optimised by the standard optimisations of the resident C compiler. Optimisation level 4. Full optimisation. No debugging information is produced. The jBC code is fully optimised and no run time checking is done on array boundaries and the like. All symbol information is removed from the final executable and all available (safe) optimisations are performed by the resident C compiler.
-JO1
-JO2
-JO3
-JO4
-JGti
10-5
intended for inclusion in other C++ source files using #include. Files INCLUDEd in this source will however be expanded in the generated include file. -JGtn Translate the file and parse INCLUDE files for data definitions and DEFC/DEFCPP statements etc. However, the jBC statement INCLUDE is translated to the equivalent #include in C++. Translate the file, which is an INCLUDEd file as per option -JGti above. However, in addition, any INCLUDE statements are translated to the equivalent #include in C++; like the -Jgtn option above.
-JGtin
When the jbc command is used to create an executable, various libraries are referenced to resolve the external symbol processing during the linking phase. The mechanism for doing this is: Look in the libraries defined by the JBCLIBFILE environment variable. Look in the libraries defined by the -lname options to the jbc program. As with cc, the name is expanded to libname.so if it exists, or libname.a otherwise. Always look in the libraries libjbc.so and libjedi.so (or libjbc.a and libjedi.a if the -JLa option used). Always look in libraries such as curses, ld, math etc. The actual list depends on the platform that jBASE is loaded onto. Use the -Jv option to get the full list. Always look in the default C library. When searching for libraries, look in the directories specified with the JBCLIBDIR environment variable. When searching for libraries, look in the directories specified with the -Ldirname option. If more than one Ldirname option is used, left to right searching is used. When searching for libraries, use the library directories as per the cc command. Always link the program with libraries specified on the command line to the jbc program in the format name.a and name.so.
10-6
JBCLIBFILE You can specify default libraries to look in by using this environment variable. These library names have the highest precedence. For example: % export JBCLIBFILE=-l comms -l dothis
jbc Examples
% jbc invoices.b The simplest form of the command compiles the jBC source program invoices.b, held in the current directory then writes the resultant executable code into the default file a.out in the same directory. % jbc -JO4 myjbc.b cfunctions.c anobject.o -o myapp This command compiles the jBC code in the file myjbc.b, the C code in cfunctions.c, and links the resultant object code with the existing object code anobject.o to produce the UNIX executable myapp. The parameter -JO4 informs the compiler that full optimisation is to be carried out on the jBC and C code. % jbc -Jalibfuncs.a func1.b func2.c obj3.o -Jdvs This command will compile the jBC source func1.b and the C source func2.o to object file. The files func1.o, func2.o and obj3.o will then be added to the archive library libfuncs.a. The -Jd option causes the object func1.o to be deleted afterwards. The -Jv option causes jbc to display all the programs it executes. The -Js option causes the time taken to execute the jbc command to be displayed. % jbc prog1.b -I$HOME/INCLUDES -I$HOME/BP -c This example shows the source code prog1.b being compiled. The -c option stops the compilation once the object prog1.o has been created. The -I option shows that any INCLUDE (or $INCLUDE) statements seen in the source prog1.b can be found by first looking in file $HOME/INCLUDES and then $HOME/BP.
The EQUATE directive instructs the pre-processor to replace any occurrence of symbol with the text substitute. The substitute text will first be checked against any existing EQUATE directives and re-substituted. This resubstitution is carried out only once to prevent infinite loops in substitution. For example: EQUATE Day TO EQUATE Monday ..... IF Monday THEN DATE()/7 TO Day = 1 ;* Day is 0 TO 6 ;* Check for Monday
;* IF today is Monday
In this example the substitute text for Monday (Day = 1) is checked against the existing EQUATE for Day. The word Day is then substituted to generate the final text DATE()/7 = 1. #define #define symbol substitute
This directive is an alternative form of the EQUATE directive above. # ifdef and #endif #ifdef .... #endif symbol
You can also create definitions at compile time using the -JCDxxx option to jbc
10-7
The #ifdef statement checks the equate (#define) definitions and if an EQUATE or #define has been declared for symbol, it allows the code up to the #endif statement to be given to the compiler. If symbol has not been defined, the pre processor will stop passing code to the compiler until it encounters the #endif statement.
10-8
BASIC Command
The BASIC command is used to create a program or subroutine object from a jBC source code. The BASIC command has been touched on already in the Programmers Reference Manual. This section is designed to give an insight into the workings of the command, and how it can be extended. Imagine the following command was executed to compile program source PROGRAM1 and subroutine source sub1.b: % BASIC BP PROGRAM1 sub1.b The BASIC command would create the object records $PROGRAM1 and sub1.o in file BP. The BP file can be any file type supported by jBASE, whether it is a hashed file, UNIX directory and so on. The steps used by BASIC are: Any supplied record keys with a $ prefix or a .o suffix are ignored. The source is moved to the current working directory as a temporary UNIX file called BASIC_nn.c, where nn is the users port number. The source is compiled using the jbc command. Assuming the compilation works, a new file BASIC_nn.o will have been created. The object file BASIC_nn.o is moved back to the original file. If the original record key had a suffix of .b, the object will be moved with a .o suffix. If not, the object will be moved with a $ prefix. The BASIC command cleans up any scratch files it created.
10-9
2 3
The following source code can be used to show the different warning levels in action. Each warning level will produce a different set of warning messages. The higher the warning level the more warning messages will be generated. SUBROUTINE abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ varx = 1 COMMON varx vary = varz FOR I = 1 TO 10 NEXT L BREAK CONTINUE format = varx "L#10" "L#20" "L#30" END END
10-10
Introduction
The jBuildSLib and CATALOG commands are both involved with creating a shared object from archive objects. The CATALOG command can also create an executable program. The CATALOG command was briefly described in the Programmers Reference Manual. This chapter explores its advanced operation.
11-1
jBuildSLib Command
This command will basically take any number of existing objects that have previously been compiled (using tools such as ar, jbc, cc, BASIC or as) and will create a shared object from the input objects. Similar functionality can be provided by the UNIX cc command, but jBuildSLib makes the interface easier as well as providing command syntax portability across different C development systems. The shared object is then accessible to other programs in two different ways: 1. By using the CALL statement from a jBC source. 2. By calling a C function from a C source or a jBC source. The syntax of the command is: jBuildSLib {-o output} {-v} {-llib} {-Iimport} {name.o} {name.so} {name.a} Where: -o output -v -llib -Iimport name.o name.a name.so The name of the output file, default is a.out Verbose mode. Specify a library using normal cc command syntax. Used by AIX system and specifies a import list. An existing compiled object. An existing archive library. An existing shared object.
Output is to file a.out, although this can be overridden with the -o option. The command also creates an export list, which is a list of all symbols contained in the new shared objects. This export list is used in 2 ways: 1. By AIX systems as an import list, when using the created shared object. 2. By the jBC run-time when executing a CALL statement. Example The example below shows two C sources being compiled into a library, and a shared object being created out of them % cc -c func1.c func2.c % ar -rv libcommon.a func1.o func2.o % jBuildSLib -o libshared.so libcommon.a In this example, the shared object libshared.so will be created, together with the export list libshared.so.el. There are three stages to using shared objects and jBuildSLib: 1. 2. 3. Create the input objects to jBuildSLib. Use the jBuildSLib command to create a shared object. Reference a shared object from a C or jBC program.
These three stages will be dealt with separately in more detail. There will be a rolling example of the use of jBuildSLib in these explanations. The example shows the creation of an application with 2 jBC programs, 3 C sources, 2 jBC subroutines, 1 compiled object and one library: program prog1.b calls C functions func1, func2 and func3, as well as the jBC subroutine sub1. It is compiled with the -JLS switch so the call to the jBC subroutine is through a direct call, rather than a runtime search through all the available shared objects. program prog2.b calls C function func4, and jBC subroutine sub2. It is compiled normally and the CALL to sub2 uses the normal mechanism of looking in all the shared objects at run-time. functions func1.c, func2.c and func3.c are C sources containing the functions func1, func2 and func3, as used by program prog1.b. They make calls to other external functions in a comms package.
11-2
libcomms.a is a comms package supplied by a third party vendor, for which only the archive library is available. It is called by C functions func1, func2 and func3. sub1.b is a jBC subroutine, called by program prog1.b sub2.b is a jBC subroutine, called by program prog2.b func4.o is a pre-compiled object, called by program prog2.b
11-3
2.
Before proceeding with the example, take a look at a parts of program prog1.b and prog2.b: prog1.b DEFC func1() DEFC func2() DEFC func3() func1() func2() func3() CALL sub1 prog2.b DEFC func4() func4() CALL sub2 Example Compile program prog1 to be able to reference functions func1, func2, func3 and subroutine sub1 using shared objects. Following compilation, the shared objects will be moved to a library directory, and the environment variable LD_LIBRARY_PATH modified so the UNIX dynamic loaded can find the shared objects. % % % % jbc prog1.b -o prog1 libfuncs.so -JLS mv libfuncs.so $HOME/lib export LD_LIBRARY_PATH=$HOME/lib:$LD_LIBRARY_PATH prog1
Note that for AIX systems the environment variable is LIBPATH. Example Compile program prog2 to be able to reference functions func4 and subroutine sub4. The function func4 is referenced as the previous example, but the subroutine sub2 is called through the jBC run-time CALL statement and needs to be placed in the JBCOBJECTLIST path. % jbc prog2.b -o prog2 libutils.so % mv libutils.so libutils.so.el $HOME/newfunc % export LD_LIBRARY_PATH=$HOME/newfunc:$LD_LIBRARY_PATH % export JBCOBJECTLIST=$HOME/newfunc:$JBCOBJECTLIST % prog2 Note that the JBCOBJECTLIST variable may not be set up in which case jBC run-time would default to $HOME/lib. Hence an alternative way of setting the JBCOBJECTLIST variable would be to: % export JBCOBJECTLIST=$HOME/newfunc:$HOME/lib In reality, for subroutine sub2 you would normally use the CATALOG command as this is much easier to use. An alternative way of creating program prog2, calling function func4 and subroutine sub2 would be: % BASIC . sub2.b % CATALOG . sub2.b
11-4
11-5
CATALOG Command
The CATALOG command is used to create an executable program from a compiled jBC program, or a shared object, accessible through the CALL statement, from a compiled jBC subroutine. The CATALOG command has been referred to in the Programmers Reference Manual. This section is designed to give an insight into the workings of the command, and how it can be extended. Imagine the following commands were executed to compile program source PROGRAM1 and subroutine source sub1.b: % BASIC BP PROGRAM1 % BASIC BP sub1.b The BASIC command would create the object records $PROGRAM1 and sub1.o in file BP. The BP file can be any file type supported by jBASE, whether it is a hashed file, a UNIX directory and so on. The CATALOG command can now be used to create an executable program PROGRAM1 and a shared object subroutine sub1 with the following command: % CATALOG BP PROGRAM1 sub1 Alternative forms of this could be: % CATALOG BP \$PROGRAM1 sub1.o or % CATALOG BP PROGRAM1.o % CATALOG BP \$sub1 This simple example shows the most common usage of the CATALOG command. There are extensions to CATALOG, but before proceeding to examine them, is worth explaining what happens when a program and subroutine are CATALOGed.
11-6
A check is made for duplicate names - in the following command only one object is processed: % CATALOG BP $sub1 sub1 sub1.o The $sub1 or sub1.o object is looked for in file BP. It is moved to the current working directory as a temporary UNIX file called BASIC_nn.o, where nn is the users port number. The symbols in the object are examined so that it can be determined as a subroutine, rather than a program. The directory $HOME/lib is created, if it doesnt already exist. The object is moved to the directory $HOME/lib/objAAA where AAA is the users account name. The file $HOME/lib/libAAA.el is examined. This contains the size and description of all objects cataloged so far. The CATALOG command will select which shared object the new object is to be placed in, or whether to allocate a new shared object. Using the $HOME/lib/libAAA.el file, the CATALOG command can determine what objects in directory $HOME/lib/objAAA will be used, along with the new object, to create a single shared object. The jBuildSLib command is invoked to create a shared object in directory $HOME/lib, something like this: % jBuildSLib -o $HOME/lib/libAAAnn.so $HOME/lib/objAAA/sub1.o $HOME/lib/objAAA/sub2.o
AAA is the users account name, nn is the number of the shared object chosen by the CATALOG command (using $HOME/lib/libAAA.el) and there can be any number of objects defined (only 2 in this example). The CATALOG command cleans up any scratch files created.
11-7
% export JBCDEV_LIB=/home/commonlib % CATALOG BP sub1.o In the above examples, you will probably need to edit the file /home/commonlib/jLibDefinition for account name, see the next topic.
If you look at file $JBCRELEASEDIR/config/jLibDefinition using any UNIX editor, it will give you more information on the layout of the file. The default values provided with jBASE will usually be sufficient for most needs. There is one circumstance when the definition file may need to be changed. This is when you are using CATALOG into the directory from more that one account The definition file contains lots of %a strings which tells CATALOG to substitute the string %a with the current users account. As this will vary between different account users, you may wish to replace the %a string in all the definitions to be some constant value (any value will do!).
11-8
Introduction
This chapter describes some of the advanced tools and techniques that may be used to run and debug your program.
12-1
Advanced Tools
-Jp
-Js -Ju
12-2
Note that this incurs a performance penalty and should be avoided if possible. Small terminal populations will probably not see much degradation, but large terminal populations performing lots of screen based activity will incur a significant penalty. -Jw Prevents the debugger being entered when warning messages are issued. By default, when a run-time error occurs and a warning message is issued, the debugger will be entered. The later section on error messages describes this more fully. This option will not affect fatal run-time errors, where the debugger will still be entered if possible. Causes the contents of the all variables in a program to be displayed at the end of a program. Turns on profiling support. This is discussed later in this chapter.
-Jx -JP
The use of the -J options is usually hidden from the application. Consider the following jBC program: 001 PRINT DQUOTE(SENTENCE()) Assume the program was started like this: % testprog -Jw -JP filename recordkey The output from the program would be: testprog filename recordkey The same is true for the SYSTEM(1000) function call. However the application can use the SYSTEM(1001) function call to obtain all the command line arguments, for example if you changed the source code to become: 001 PRINT DQUOTE(CHANGE(SYSTEM(1001),CHAR(254), )) And ran it the same way, the output would be: testprog -Jw -JP filename recordkey. Note that the reason for using the CHANGE function is that SYSTEM(1000) and SYSTEM(1001) return the command line with the arguments delimited by an attribute mark, whereas the SENTENCE function delimits the arguments with a space character.
12-3
Advanced Tools
jPMLMsg Command
The basic purpose of jPMLMsg is to allow a root user to pass messages to a particular port number. These messages are all pre-defined, so only a known set of commands can be executed. The jPMLMsg command will be extended in future releases of jBASE, but for the present it is restricted to allowing debug requests to be sent from a root user to another jBASE program. The syntax for jPMLMsg is: % jPMLMsg {-v} {-pPid} PortNo Command {arg} {Command {arg} ...} Where: -v -Ppid PortNo arg Command Verbose, message to stdout. Restrict message to process id Pid instead of all pids for port PortNo. The port number to send the message to. Optional command argument, depends upon message. Can be one or more of the following: ENTERDEBUG causes the port to enter the debugger regardless of the status of the BREAK flags, TCL Restart and END restart. Note that the debugger will only usually be entered on the next command executed. If a terminal is at an INPUT statement, the debugger is entered immediately. If, for example, the terminal is at a SLEEP statement, you will need to press the INT key (or send the SIGINT signal) to enter the debugger. EXITDEBUG restores the original status of the environment to that prior to a ENTERDEBUG. DEBUGTTY ttyname causes the debugger tty output to be redirected to device 'ttyname'. Example A terminal needs debugging, but you cannot debug it because TCL restart is enabled, or BREAK-OFF has been set to disable break key etc. So, you need to not only over-ride the security in the program, but also debug it from a different terminal. Enter the following command: % jPMLMsg -v 47 DEBUGTTY /dev/pts005 ENTERDEBUG This assumes that: The port number you want to debug is 47. The programmer is logged on as root on device /dev/pts005. The account that port 47 is logged on to has read/write permissions for device /dev/pts005. The terminal at /dev/pts005 is currently inactive, for example 'sleep 99999'
A similar example to start debugging another process from the device you are logged on to is: % jPMLMsg -v 47 DEBUGTTY `tty` ENTERDEBUG ;sleep 999
12-4
Profiling
There are simple profiling tools available with UNIX SVR4 and AIX systems. The library calls are not universally supported so not all jBASE platforms will support this profiling feature. By default, no profiling is done in the program. Programs do not have to be compiled in any special manner to enable profiling for that program. All that is required is that the programs were not compiled with optimisation, as this discards the debug information which is required for profiling. The mechanism works by receiving a signal at every clock tick and keeping note of where the program was when the signal arrived. Thus, for the profiling to be accurate, the application must be run for a relatively long time. It will not show particularly good results if, for example, a program executes in less than a second. Several minutes or longer is preferred.
Enabling Profiling
There are two ways of enabling profiling for a program. 1. Use the -JP option when the program is executed. For example: % MAINPROG -JP Filename This generates a profiling file called 'jprof' in the users current directory. Note that when the application stops, or chains to another program, profiling is terminated. 2. Set the environment variable JBCPROFILE. For example: % JBCPROFILE=0 MAINPROG Filename This generates a different profiling file for each process executed, while the environment variable is active, in the format: 'jprof_pid_n'. Where 'pid' is the process id, and 'n' is an incrementing number starting at the value 'JBCPROFILE' was set to. This allows processes that do a CHAIN or ENTER (and so retain the same process id) to still generate different profiling files. The profiling file generated will only contain information about user CPU time. The time spent in kernel system calls is not included. Therefore, doing a lot of file I/O (especially for j1 files), means that this time will not be included in the profiling statistics. Note that time spent in j2 files which are in memory (when there are no frame faults) will be counted, as this is user CPU time.
12-5
Advanced Tools
The -n option can be used to split the report into file names. For example, if the profiled program called lots of subroutines, each subroutine would be reported separately. By default, the section of the application that caused the most CPU usage would be reported first. If the -n option is used with the -i option, the section of the application that caused the least CPU usage will be reported first.
Example of Profiling
Imagine the source 'test1.b' below has been edited into file BP, where BP is a regular UNIX directory. Notice the INCLUDE of another source file 'test2.b'. OPEN "fb1" TO DSCB ELSE STOP 201,"fb1" PRINT "Phase 1 -- start" S1 = SYSTEM(9) FOR Id = 1 TO 100 Rec = "" FOR I = 1 TO 100 Line = "" FOR J = 1 TO 20 Line := CHAR(SEQ("A")+RND(26)) NEXT J Rec<I> = Line NEXT I WRITE Rec ON DSCB,Id NEXT Id PRINT "Phase 1 -- end, CPU = ":SYSTEM(9)-S1 INCLUDE test2.b PRINT C1:" records in file fb1" PRINT "End" The program can be created normally with the following command: % cd BP % jbc test1.b -o ../test1 % cd .. or it can be created with BASIC and CATALOG: % BASIC BP test1.b % CATALOG BP test1.b By default, when the program is run, no profiling will take place. Now run the program with the -JP switch to create a file 'jprof': % test1 -JP We can now examine the profile file with the 'jprof' command, using the -f option to generate optional source code listings from the file BP. % jprof -f BP jprof Profile of program test1 from profile jprof Source Line Ticks % Source test2.b test1.b test1.b test2.b test1.b test2.b test1.b test2.b test1.b test2.b 8 9 11 7 10 9 13 5 7 10 166 160 128 28 9 5 3 2 2 1 32.93 31.74 25.39 5.55 1.78 0.99 0.59 0.39 0.39 0.19 Page 1
READ Rec FROM DSCB,Key EL Line := CHAR(SEQ("A")+RND( Rec<I> = Line WHILE READNEXT Key DO NEXT J C1++ WRITE Rec ON DSCB,Id SELECT DSCB Line = "" REPEAT
The -i option would sort the output with incrementing Ticks counts.
12-6
The -n option would additionally sort it by file name, so the 'test1.b' entries will be displayed separately to the test2.b entries. Note that the output shown here is only a small sample of the real output.
12-7
Advanced Tools
By default, the debug status is not exported to any other programs that are executed via the PERFORM, EXECUTE, CHAIN or ENTER statement. Often a developer will not know which program inside an application is producing errors, and may want to debug a number of programs. By setting the JBCDEBUGGER environment variable (to any value), when any jBASE program is started, it will immediately enter the debugger. This means the developer can start an application and debug each program in the application without the need to add DEBUG statements or amend the code in any way. This functionality can also be initiated by using the -JD option when a jBASE program is first started. In fact, all the -JD option does is to set the JBCDEBUGGER environment variable and then process the same as the -Jd option.
12-8
Terminal Redirection
Quite often an application works in a full screen data entry format. When a developer debugs this type of application, the screen that is being generated by the application becomes corrupted due to interaction with the debugger. Whether this is a problem or not depends on the type of application and the issue you are trying to resolve. As an example, you may want to find the point at which the cursor moves unexpectedly from one field on the screen to another, or to find out when unexpected character sequences are printed. These types of problem are harder to resolve when the screen format is corrupted by the debugger while you are trying to find the cause of the original screen corruption. A jBASE program is a normal UNIX program so you could always re-direct the output to another terminal. For example: % MAINPROG > /dev/pts004 < /dev/pts004 In this example, the keyboard input and terminal output will be to terminal /dev/pts004, but unfortunately the debugger interaction will also take place on /dev/pts004. The solution is to allow just the debugger output to be re-directed to another terminal. This can be done using either the -Jr option when an application starts, or by using the r command once you are inside the debugger. The following example runs a program from one terminal, but debugs it from another terminal. Let us assume the terminal for debugging is /dev/pts004, and the terminal to run the application from is /dev/tty01s. From the debug terminal /dev/pts004, halt any other activity:. % sleep 99999 From the program terminal, start the program: % MAINPROG -Jr/dev/pts004 -Jd You are now debugging from terminal /dev/pts004, but the application still works with /dev/tts01s. Some points to remember when using this mechanism: Ideally both terminals should be logged in to the same account. This will help prevent problems with the permissions of the tty devices. If they are logged in to different accounts, you may find the debugger in /dev/tty01s does not have the correct permissions to write to file /dev/pts004. The terminal debugging the application should never be running anything else that requires terminal input. For example, if the terminal /dev/pts004 still had the shell active, any keyboard data that is received might go to the jBASE debugger or it might go to the shell. A good solution is to use the sleep command, as in the example above. The debugger output can also be re-directed with the r command, as well as the -Jr command when the program is first started. To enter the debugger other than on an error or DEBUG statement, you need to generate a SIGINT or SIGQUIT from the terminal that is running the application, and not the terminal where the debugger is running. In the above example, this would be from terminal /dev/tty01s. To get the best out of the debugger, both terminals should be of the same terminal type. If not, you may find a few of the debugger features dont display properly - the debugger will use the terminal type the application is running under to provide its terminal control characters.
12-9
Advanced Tools
jshow Program
When running a jBASE application there can be confusion about three components of execution: 1. 2. 3. When a program is executed, whereabouts in the UNIX system does the program get loaded from? It could be from the users Master Dictionary file, or from any directory in the PATH environment variable. When a file is opened, whereabouts in the UNIX system is the actual file to be found? It could be from any component in the JEDIFILEPATH environment variable, or from the resolution of a Q pointer and so on. When a SUBROUTINE is called, what library is the subroutine to be found in? It could be any shared object in the JBCOBJECTLIST environment variable, or the defaults for this variable, or from the $JBCRELEASEDIR/lib directory and so on.
A common experience amongst developers is to alter part of an application and then to wonder why the change has not taken effect. Another is to run an application and wonder how seemingly impossible results have been generated from the current files. The answer to a lot of these questions is that the program being loaded, file being opened or subroutine being called is not the one you expected. The jshow program can help you to resolve these problems. The syntax of the command is: jshow {-fhpsv} Name {Name ...} Where: -f -h -p -s -v Restricts the search to file names. Displays the help screen. Restricts the search to executable programs. Restricts the search to subroutines only. Verbose mode.
Name Is the name of the object to find. By default jshow will take each Name supplied and first see if it exists as a file, then if it exists as a jBC SUBROUTINE, then if it exists as an executable program. If any of the above are found, jshow displays the file type and the path the object type was found on. You can restrict the search to just jBASE files using the -f option. Similarly, you can use the -p and -s options to restrict the search to executable programs and subroutines respectively. The program uses all the environment variables that a real jBASE program would use. For example, if the variable JBCOBJECTLIST is not set, it uses the same defaults. If the file JEDIFILENAME_MD is set, it uses that to resolve Q pointers when looking for files, and to look for jCL scripts when looking for executable programs. If duplicates are found, they will be displayed with a warning symbol (DUP!!). This is not always an error, it may be a "feature" of the development environment. It is up to the developer to determine if the duplicate is expected or not. The -v option shows the path that the run-time would take while looking for files, subroutines and executable programs. Example 1 Find the exact location where file CUSTOMERS is being opened: % jshow -f File: CUSTOMERS /home2/live/DATA/CUSTOMERS
This shows that if a jBC program were to execute: OPEN "CUSTOMERS" TO .... The file open would succeed, and the actual UNIX file opened would be /home2/live/DATA/CUSTOMERS.
Advanced Tools Manual 12-10 Advanced Programmers Reference
Example 2 Find which shared object the subroutine INITVAR is being loaded from: % jshow -s INITVAR Subroutine: Subroutine (DUP!!): /home2/live/lib/liblive0.so /home2/live/lib/libfb10.so
This example shows that there are two versions of the subroutine INITVAR. The jBASE run-time will always use the first occurrence, ignoring any subsequent occurrences. The developer should determine if the correct version is being loaded. Example 3 The program MAIN is not behaving as expected. Use jshow to gain more information: % jshow MAIN jCL script: Executable (DUP!!): /home2/live/MD/MAIN /home2/live/bin/MAIN
In this example we can see that program MAIN exists as both a jCL script and as an executable program. There is a clear case of conflict here, and it is possible the application is using the wrong version. The run-time will execute the jCL script in preference to the executable program. Example 4 Find out as much as possible about the object PIPE, and report the paths that were used when searching for it. % jshow -v PIPE File path: File path: File: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine object: Subroutine: MD Name: Execute path: Execute path: Execute path: Execute path: Execute path: Execute path: Execute path: Execute path: Execute path: Execute path: Execute path: jCL script: /home2/live . /home2/live/PIPE main() /home2/live/lib/liblive0.so /home2/live/lib/fb1.so /home2/live/lib/libfb10.so /usr/jbc3.0/lib/libinternal.so /usr/jbc3.0/lib/libutils.so /usr/jbc3.0/lib/libqueries.so /usr/jbc3.0/lib/libjpq.so /usr/jbc3.0/lib/libjrem.so /usr/jbc3.0/lib/libjconnect.so /home2/live/lib/liblive0.so /home2/live/MD /usr/jbc3.0/bin /usr/bin /usr/merge/dosroot/ubin /usr/ccs/bin /home2/live/tools /usr/local/bin /opt/bin /home2/live/bin . /usr/ccs/bin /usr/ucb /home2/live/MD/PIPE
12-11
Advanced Tools
jtic Program
The jtic program takes a description of the capabilities and creates a binary definition of the file in similar fashion to the UNIX tic program. It is called as: % jtic {-x} {-v} {DescFile} where: -x -v The descriptions are the extended capabilities. Verbose mode.
By default, the program jtic will take a source description file called terminfo.src and assume it contains standard terminfo names. The output will be to a file called /usr/lib/terminfo/x/xyz, where x/xyz depends upon the terminal name as contained in the description file. You can use any alternative description file to terminfo.src by specifying the description file name on the command line. You can specify an alternative output directory to /usr/lib/terminfo by amending the TERMINFO environment variable. However, when you run a jBC program that accesses these definitions in an alternative directory, the TERMINFO variable needs to match that when the definition was compiled. By default the jtic program assumes the description file contains standard terminfo definitions such as cuu1=\E[1A, cols#80, If you want to create a binary with the extended capabilities, use the -x option. Remember when running jtic you will probably require root privileges to write to the /usr/lib/terminfo directory.
xon Integer value. The name of the definition followed by # followed by the value. Example: cols#132 String value. The name of the definition followed by = followed by the string value. jtic supports all the functionality of tic, such as defining characters 1 to 26 using ^A through ^Z, specials such as \E for escape, \s for space, \t for tab. It also supports the if/else/endif structure and logical/arithmetic operations supported by tic. Example: if_prtr_letter=\E[02l, use=name. Resets the definition to that of a previously defined name in the definition. You can use this to create a terminfo definition for one or more names, and then base subsequent definitions on that. An example of using this can be found in the source $JBCRELEASEDIR/src/prism. Example: use=ansi The comments and terminal names must all start at column 1. The binaries, integers, strings and use=name must all have a leading tab character. There can be more than one binary, integer or string on a line, and each definition should be delimited by a comma.
Extended Capabilities
The table below shows the extended capabilities.
12-13
Advanced Tools
Note that they are all strings - there are no binaries or integers. Within a jBC program, it is assumed that the strings are all string constants. This means that they are not passed through the tparm() function for conditional arithmetic operations and so on. The first column Access shows how to access the capability in a jBC program. The second column Long name shows the name to use in the description file that is compiled using the jtic program. The third column Description is a description of the effect. Access @(-53) @(-54) @(-55) @(-59) @(-27) @(-28) @(-29) @(-30) @(-32) @(-33) @(-47) @(-48) @(-60) @(-61) @(-62) @(-63) @(-64) @(-65) @(-66) @(-70) @(-71) @(-72) Long name if_slave_only if_crt_type if_crt_graphics if_crt_cuprot if_crt_132 if_crt_80 if_crt_dwide if_crt_swide if_crt_sron if_crt_sroff if_crt_udhdw if_crt_bdhdw if_prtr_executive if_prtr_a4 if_prtr_monarch if_prtr_comm10 if_prtr_interntldl if_prtr_reset if_prtr_envfeed if_prtr_letter if_prtr_legal if_prtr_chgcpy Description Send data to slave printer only. No VDT display Returns VDT terminal type Returns a 1 if Regis graphics available Clear all unprotected fields Switch VDT to 132 column mode Switch VDT to 80 column mode Display double wide characters Display single wide characters Set-up scrolling region. (Enter ? for help) Reset scrolling region to normal Top half of double height line Bottom half of double height line Change paper size to executive Change paper size to A4 Envelope type "Monarch" Envelope type "Commercial 10" Envelope type "International DL" Reset printer defaults Envelope feeder Change paper size to letter Change paper size to legal Change number of copies to print on Laser
Access @(-73) @(-74) @(-75) @(-76) @(-77) @(-78) @(-79) @(-80) @(-81) @(-82) @(-83) @(-84)
Long name if_prtr_cpwo1 if_prtr_spcol if_prtr_sprow if_prtr_utray if_prtr_ltray if_prtr_portrt if_prtr_land if_prtr_simplx if_prtr_duplxl if_prtr_duplxs if_prtr_macro if_prtr_setdef
Description Compressed print for Service WO form 1 Start printing at specified column Start printing at specified row Use Upper Tray Use Lower Tray Portrait orientation Landscape orientation Simplex binding Duplex, long edge binding Duplex, short edge binding Call MACRO Set default ( Font size, HMI, VMI )
12-14
@(-85) @(-86) @(-87) @(-88) @(-89) @(-90) @(-91) @(-92) @(-93) @(-94) @(-95) @(-96) @(-97) @(-98) @(-99) @(-100) @(-101) @(-102)
if_prtr_lpi2 if_prtr_lpi3 if_prtr_lpi4 if_prtr_lpi6 if_prtr_lpi8 if_prtr_lpi12 if_prtr_dwide if_prtr_swide if_prtr_96 if_prtr_pld if_prtr_plu if_prtr_suon if_prtr_sbon if_prtr_ssoff if_prtr_40 if_prtr_48 if_prtr_ff if_prtr_80
2 lines per inch 3 lines per inch 4 lines per inch 6 lines per inch 8 lines per inch 12 lines per inch Double wide mode Single wide mode 96 column mode 1/2 line down 1/2 line up Superscript mode Subscript mode Superscript and subscript off Double wide for 80 column mode (5 pitch) Double wide for 96 column mode (6 pitch) Top of form 80 column mode (10 pitch)
Access @(-103) @(-104) @(-105) @(-106) @(-107) @(-108) @(-109) @(-110) @(-111) @(-112) @(-113) @(-114) @(-115) @(-116) @(-117) @(-118) @(-119) @(-120) @(-121) @(-122) @(-123) @(-124) @(-125)
Long name if_prtr_132 if_prtr_bold if_prtr_ul if_prtr_norm if_prtr_hmi if_prtr_vmi if_prtr_pson if_prtr_psoff if_prtr_1key if_prtr_2key if_prtr_3key if_prtr_4key if_prtr_6key if_prtr_7key if_prtr_8key if_prtr_9key if_prtr_cvd if_prtr_mvd if_prtr_type if_prtr_fvd if_prtr_chd if_prtr_mhd if_prtr_fhd
Description 132 column mode (16 pitch) Bold Underline Turn off bold and underline Set horizontal motion index to U-1 Set vertical motion index to U-1 Proportional spacing on Proportional spacing off Linefeed and backspace Linefeed Linefeed and space Backspace Space Negative linefeed and backspace Negative linefeed Negative linefeed and space Coarse vertical distance ( 1 line = 1 inch ) Medium vertical distance ( 1 line = 1/6 inch ) Returns slave/printer type Fine vertical distance ( 1 line = 1/48 inch ) Coarse horizontal distance ( 1 space = 1 inch ) Medium horizontal distance ( 1 space = 1/12 inch ) Fine horizontal distance ( 1 space = 1/120 inch )
12-15
Advanced Tools
@(-126)
if_prtr_status
12-16
The file the messages are taken from is $JBCRELEASEDIR/jbcmessages. Each record in the file is an individual message, and the record key is the error message number. For example: STOP 201,CUSTOMERS will display the message as defined by record key 201 in file $JBCRELEASEDIR/jbcmessages. You can modify this string, or examine it, using any jBASE editor, for example: % ED $JBCRELEASEDIR/jbcmessages 201 201 TOP . P 001 ** ERROR [ 201 ] ** ^NEWLINE^Unable to open file %s^EXIT51^ . exk The entries in the file are made up of three basic components: 1. 2. Static text. This is the text that is neither a parameter nor a control sequence and will be displayed exactly as it defined. Parameters. These are defined using the format of the printf() function, such as %s or %d.
Most of the parameters defined in the STOP or ABORT statements will be of the format %s. If you modify them, they should still be defined as a string. For example you could change %s to %-20s. The order of the parameters is the same as passed from the STOP or ABORT statement. 3. Control sequences. These are special control sequences interpreted by the run-time. They have the format ^XXXX^. In the example above, ^NEWLINE^ would be replaced by a 0x0a new-line character on output.
For a more complete description of the record layout, see the file $JBCRELEASEDIR/jbc.init.err. Remember that if you modify any error messages, you may need to save and restore them when you install a new version of jBASE. The error messages are held initially in a temporary form in the file $JBCRELEASEDIR/jbc.init.err. This is the internal format. Before jBASE is loaded, the messages in this file will be converted to file $JBCRELEASEDIR/jbcmessages by the command jmakeerr. The file $JBCRELEASEDIR/jbc.init.err is left on the directory as a sort of README file to provide a description of the error message format.
12-17
Advanced Tools
The first 2 lines are formatted by the error message ZERO_USED from file $JBCRELEASEDIR/jbcmessages. This message is a warning message because it contains the string ^WARNING^. The debugger is therefore triggered and the 3rd line is generated by the debugger as it is entered. A common requirement is to change an error message so that the debugger is not entered when the message is displayed. You can achieve this by changing the error message from a warning to a normal message through the ED editor: % ED $JBCRELEASEDIR/jbcmessages ZERO_USED TOP . <RETURN> 001 Invalid or uninitialised variable -- ZERO USED, ^NEWLINE^Var ^VARNAME^, Line ^LINENO^, Source ^SOURCENAME^ ^WARNING^ .R/^WARNING^// 001 Invalid or uninitialised variable -- ZERO USED, ^NEWLINE^Var ^VARNAME^, Line ^LINENO^, Source ^SOURCENAME^ . FI Record ZERO_USED written to /usr/jbc/jbcmessages
jmakeerr Command
The jmakeerr command is provided for converting error messages in alternative formats to the format required by jBASE. The originating format can be either jBASE internal format, or legacy type ERRMSG file format. The command is therefore mainly used to convert error messages from their ERRMSG file format to the format required by jBASE. The command syntax is: jmakeerr inputfile {Key {Key ..}} | * {(MNOR)} TO : (outputfile Where: inputfile is the input file containing the original records to be converted. outputfile is the output file for the converted records. Key is a list of record keys to convert. * specifies that all records from inputfile are to be used. (M) indicates a migration from legacy versions of ERRMSG. (N) converts numeric record keys only. (O) overwrites any existing records in outputfile. (R) lists record keys as they are converted. A typical use would be for a user to T-DUMP the file ERRMSG from their existing system, to T-LOAD it back into a jBASE system and then use the jmakeerr command as follows: % SELECT ERRMSG EQ \USER]\ 97 Records selected % jmakeerr ERRMSG \(MO TO: (/usr/jbc/jbcmessages In the above example, the user is selecting all the error messages they have created themselves, and then using jmakeerr to convert them from the original ERRMSG file and format to the file and format used by jBASE, namely /usr/jbc/jbcmessages.
12-18
Index
# # ifdef, 10-7 #define, 4-16, 10-7, 10-8 #endif, 10-7 #include, 4-3, 4-5, 4-6, 4-7, 4-9, 4-10, 4-11, 4-12, 4-13, 4-14, 4-16, 4-17, 4-20, 8-1, 8-5, 10-3, 106, 10-6 . .b. See file types .el. See file types .o. See file types .profile file, 2-6, 2-7, 2-10, 2-11, 2-12, 3-6, 8-5 .so. See file types A ACCOUNT-RESTORE, 2-5, 2-8 ACCOUNT-SAVE, 2-4 Ajar. See File Ajar Processing API calls, 8-1 API Calls, 1-4 B BASIC, 1-3, 1-4, 2-5, 2-10, 3-1, 3-2, 3-3, 3-4, 3-11, 5-2, 6-1, 9-1, 10-1, 10-10, 11-2, 11-4, 11-6, 126 BASIC Command, 10-9 C C Extensions, 1-3, 4-1 C function definition, 4-6 C Functions, 4-1, 4-16, 4-18 CALL, 3-1, 3-7, 4-16, 4-18, 7-3, 10-5, 11-2, 11-4, 11-6, 12-2 calloc, 7-7 CATALOG, 1-3, 1-4, 2-5, 2-11, 3-1, 3-4, 3-6, 3-7, 3-8, 3-9, 3-10, 3-11, 5-2, 9-1, 10-9, 11-1, 11-4, 12-6 CATALOG Command, 11-6 cc, 4-3, 7-2, 8-5, 10-1, 10-2, 10-3, 10-4, 10-6, 11-2, 11-3 CC, 10-6 CHDIR_IB, 4-20, 4-21 CLEAR, 7-9, 7-27 CLOSE, 7-9, 7-12, 7-14, 7-16 Compiling, 2-10, 3-3, 4-18, 8-5 CONV_FB, 4-13 CONV_FI, 4-12, 4-13 CONV_IB, 4-5, 4-13 CONV_IF, 4-13 CONV_SB, 4-13, 4-14, 4-20 CONV_SFB, 4-6, 4-7, 4-14 CONVLEN_IB, 4-13, 4-20 CONVTYPE_IB, 4-14, 4-15 COPY, 2-9, 3-5, 3-7 COUNT_IBB, 4-11 D Database Drivers, 1-3, 7-1 DCOUNT_IBB, 4-9, 4-11, 4-20 Debug, 10-5 Debugging, 12-8 DELETE, 2-9, 7-9, 7-26 DELETE-CATALOG, 2-12 E ED, 3-1, 3-2, 12-17, 12-18 ENABLE-LOGONS, 3-9, 3-10 Environment Variables, 8-8, 10-6 EQUATE, 10-7, 10-8 ERRMSG, 12-18 Error Message File, 12-17 Executable Paths, 1-3 Exporting Applications, 2-4 External libraries, 4-1 F File ajar processing, 7-8 File Ajar Processing, 7-5, 7-12, 7-13 file types .b, 3-3, 10-9 .el, 2-11, 3-7, 3-10 .o, 2-11, 3-3, 3-4, 10-3, 10-4, 10-9, 11-3, 11-6, 11-7
19
Index
.so, 2-11, 3-7 FLOAT, 4-3, 4-4, 4-5, 4-11, 4-12, 4-13, 7-22 free, 7-7, 7-17, 8-2 Function Prototypes, 4-4 H HOME, 1-3, 2-6, 2-11, 2-12, 3-2, 3-4, 3-5, 3-6, 3-7, 3-9, 3-10, 7-2, 7-3, 8-8, 10-7, 11-4, 11-6, 11-7, 11-8, 12-2, 12-5 I ICONV, 1-3, 4-1, 5-1 ICONV Extensions, 5-1 IJU, 2-7, 3-6 Importing Accounts, 2-6 Importing Applications, 2-5 INCLUDE, 2-10, 6-1, 6-2, 6-3, 6-4, 6-5, 6-7, 6-8, 6-9, 10-3, 10-5, 10-6, 10-7, 12-6 INDEX_IBBI, 4-9, 4-11 infocmp, 12-13 INHIBIT-LOGONS, 3-9, 3-10 INIT, 7-9, 7-11, 7-12 INT, 4-2, 4-4, 4-5, 4-6, 4-7, 4-12, 4-13, 7-22, 12-4 INT32, 4-3, 4-4, 4-5, 4-6, 4-7, 4-8, 4-9, 4-10, 4-11, 4-12, 4-13, 4-14, 4-15, 4-20 ioctl, 1-3, 6-1, 7-4, 7-31, 8-19 IOCTL Function, 1-3, 6-1, 7-1, 7-4, 7-9, 7-12, 722, 7-23, 7-25, 7-31, 8-19 J j1, 1-1, 6-8, 7-1, 12-5 j2, 1-1, 6-8, 7-1, 12-5 jbackup, 1-1, 6-5 jbc, 1-1, 1-4, 2-10, 3-2, 4-2, 4-3, 4-7, 4-16, 4-18, 61, 8-5, 10-1, 10-9, 10-10, 11-2, 11-3, 11-4, 115, 11-6, 12-6, 12-13 jBC, 1-1, 1-3, 4-9 jbc Command, 10-2 jBC Run-time Options, 12-2 JBC.h, 6-1, 6-2, 6-3, 6-4, 6-5, 6-7, 6-8, 6-9 JBC_COMMAND_GETFILENAME, 6-1, 6-2 JBCDEBUGGER, 12-2, 12-8 JBCLIBDIR, 10-6, 10-7 JBCLIBFILE, 10-6, 10-7 JBCLOGNAME, 2-6, 2-12 JBCOBJECTLIST, 2-12, 3-7, 3-9, 3-10, 7-3, 8-8, 9-3, 11-4, 12-10 JBCPORTNO, 2-6 JBCPROFILE, 12-5 JBCRELEASEDIR, 3-7, 4-3, 4-19, 6-1, 7-1, 7-2, 74, 8-1, 8-5, 10-4, 10-6, 11-8, 12-10, 12-13, 1217, 12-18 jBuildSLib, 1-4, 3-7, 4-18, 7-2, 11-7 jBuildSLIb, 11-1 jBuildSLib Command, 11-2 jCL, 1-1, 3-6, 12-10, 12-11
jED, 3-1 jEDI, 1-1, 1-3, 1-4, 2-11, 3-3, 4-20, 6-2, 6-9, 7-1, 8-1, 10-1, 10-9, 11-7, 12-2 jEDI API calls, 8-1 jEDI Database Drivers, 7-1 jedi.h, 7-1, 7-4, 7-12, 7-23, 7-25, 8-1, 8-5 JEDI_LOCK, 7-7 JEDI_LOCK_NOWAIT, 7-7, 7-8 JEDI_TRANSLOG_COMMAND_ABORT, 8-4, 8-6 JEDI_TRANSLOG_COMMAND_END, 8-6 JEDI_TRANSLOG_COMMAND_QUERY, 8-1, 8-4, 8-7 JEDI_TRANSLOG_COMMAND_START, 8-6 JEDI_UNLOCK, 7-7, 7-30 JEDI_UNLOCK_ALL, 7-7, 7-8, 7-30 JediCalloc, 7-7 JediClearFile, 7-9, 8-3, 8-21 JediClose, 7-9, 8-3, 8-11 JediDelete, 8-17 JEDIFILENAME_MD, 2-10, 3-6, 8-8, 12-10 JEDIFILENAME_SYSTEM, 8-8 JediFileOp, v, 8-3, 8-23 JEDIFILEPATH, 6-2, 7-3, 8-8, 8-9, 8-10, 12-10 JediFree, 7-7, 7-17, 7-18, 7-21 JediIOCTL, 7-9, 8-3, 8-19 JediLock, 7-9, 8-3, 8-18 JediMalloc, 7-7, 7-15, 7-16, 7-20, 7-23 JediOpen, 8-3, 8-8, 8-9, 8-10, 8-15, 8-16 JediOpenDeferred, 8-3, 8-10 JediPerror, v, 8-3, 8-22 JediReadMalloc, 7-7, 7-21, 7-23 JediReadnext, 7-9, 8-3, 8-14 JediReadRecord, 7-9, 8-3, 8-15, 8-18 JediRealloc, 7-7 JediReinitialise, v, 8-3, 8-8, 8-25 JediSelect, 7-9, 8-3, 8-12, 8-13 JediSelectEnd, 7-9, 8-3, 8-13 JediSelectPtr, 7-15, 7-16, 7-17, 7-18, 7-19, 8-12, 813, 8-14 JediStrdup, 7-7 JediSync, 7-9, 8-3, 8-20 JediSystemLock, 7-7, 7-12, 7-29, 7-30 JediWriteRecord, 7-9, 8-3, 8-16 JIOCTL_COMMAND_CONVERT, 6-3, 6-4 JIOCTL_COMMAND_FILESTATUS, 6-1, 6-5 JIOCTL_COMMAND_FINDRECORD, 6-7 JIOCTL_COMMAND_HASH_LOCK, 6-9 JIOCTL_COMMAND_HASH_RECORD, 6-8 jLibDefinition, 11-8 jLibDefinition file, 2-11 JLibECOUNT_IBB, 4-11 JLibFOPEN, 7-3 jLP, 1-1 jmakeerr, 12-17 jmakeerr Command, 12-18 jPMLMsg Command, 12-4 jpp, 10-2, 10-7 jQL, 1-1
20 Advanced Programmers Reference
Index Manual
jrestore, 1-1 jsh, 2-12, 3-5 jshow, 12-10 jsystem.h, 4-3, 4-5, 4-6, 4-7, 4-9, 4-10, 4-11, 4-12, 4-13, 4-14, 4-16, 4-17, 4-20, 7-1, 8-1, 8-5 jtic, 12-12, 12-13, 12-14 jtic ,Program, 12-12 L LD_LIBRARY_PATH, 2-7, 4-19, 8-5, 8-8, 11-4 LIBPATH, 4-19, 8-5, 8-8, 11-4 Linking, 4-18, 8-5 LOCK, 7-7, 7-9, 7-14, 7-23, 7-25, 7-26, 7-29, 8-18 LOGOFF, 3-9, 3-10 M Macro Pre-processor - jpp, 10-7 Macros, 4-11 make command, 9-1 Makefile, 9-1 makefiles, 9-1 Makefiles, 1-4 malloc, 7-7, 8-2 Migration, 1-3 O OCONV, 1-3, 4-1, 4-12, 5-1, 12-13 OCONV Extensions, 5-1 OPEN, 6-1, 6-2, 6-3, 6-4, 6-5, 6-7, 6-8, 6-9, 7-1, 73, 7-5, 7-6, 7-7, 7-8, 7-9, 7-11, 7-16, 7-17, 7-21, 7-22, 7-23, 7-24, 7-25, 8-1, 8-2, 12-6, 12-10 P PATH, 2-7, 2-11, 3-5, 3-6, 3-9, 9-3, 12-10 Performance, 4-1 Portability, 4-1 PORTBAS, 2-8, 2-9, 2-10 PROC, 2-1 ProcessAjar, 7-5, 7-12 ProcessReopen, 7-5, 7-13 Profiling, 12-5 PWD, 8-3, 8-8 R READ, 6-4, 6-7, 7-1, 7-3, 7-7, 7-9, 7-12, 7-21, 725, 7-29, 8-1, 8-15, 8-18, 12-6 READNEXT, 7-1, 7-9, 7-15, 7-17, 7-19, 8-12, 12-6 realloc, 7-7, 8-2 Record Locking, 7-7, 7-29, 8-18 register variables, 4-9, 4-16, 4-20 S security, 2-1, 2-6, 7-11, 12-4 Security, 2-2 SELECT, 7-1, 7-9, 7-14, 7-15, 7-17, 7-19, 8-12, 814, 12-6, 12-18
SELECTEND, 7-9, 7-15, 7-16, 7-17, 8-12 shared library objects, 2-11, 2-12 SMA save format, 2-4 SQL, 1-1, 10-3, 10-5 stdio.h, 4-3, 8-1 STORE_BBB, 4-11 STORE_BBF, 4-11 STORE_BBI, 4-12 STORE_BBS, 4-12 STORE_VBB, 4-11, 4-20 STORE_VBF, 4-11 STORE_VBI, 4-10, 4-12 STORE_VBS, 4-4, 4-6, 4-10, 4-12, 4-13, 4-15, 421 strdup, 7-7, 8-2 STRING, 4-3, 4-4, 4-5, 4-6, 4-7, 4-8, 4-10, 4-12, 413, 4-14, 4-15, 4-21, 7-22 STRING_INITIALISE_REG_VB, 4-10, 4-14, 415, 4-20 STRING_INITIALISE_USER_VB, 4-10, 4-14, 421 STRING_MAKE_NEW_VBIS, 4-7, 4-15 STRING_RELEASE_EXT_VB, 4-10, 4-14, 4-15, 4-21 STRING_RELEASE_REG_VB, 4-15 STRING_RESIZE_VBI, 4-15, 4-20 SUBROUTINE, 1-3, 2-9, 3-1, 3-4, 4-16, 4-18, 5-1, 5-2, 5-3, 10-10, 11-6, 12-10 SYNC, 7-9, 7-34 T Terminal Redirection, 12-9 terminfo, 12-12 Transaction Boundary, 7-8, 8-6 type conversion, 4-5, 4-12 U UNIX, 1-1, 1-2, 1-3, 1-4, 2-6, 2-7, 2-11, 3-1, 3-4, 4-14, 12-5, 12-12, 12-13 UNIX executable, 3-4 user variables, 4-9 user-exit, 5-3 V VAR, 4-3, 4-4, 4-5, 4-6, 4-7, 4-9, 4-10, 4-11, 4-12, 4-13, 4-14, 4-15, 4-17, 4-20 VAR_TYPE_FILE, 4-14 VAR_TYPE_FLOAT, 4-14 VAR_TYPE_FLOAT_INT, 4-14 VAR_TYPE_INT, 4-14 VAR_TYPE_SELECT, 4-14 VAR_TYPE_STRING, 4-14, 4-16 W Windows, 1-1, 1-2, 12-12 Windows 95, 1-1 Windows NT, 1-1, 12-12
21
Index
Index Manual
22