Vous êtes sur la page 1sur 200

jBASE Advanced Programmers Reference Manual

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

Advanced Programmers Reference Manual

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

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

Using the Manual


Summary of Chapters
gives an overview of jBASE and the advanced techniques described in this manual. 2 Migration shows how to perform a full migration from an existing alternate development environment. 3 Execute Paths gives details on the where and why of positioning executable code. 4 C Extensions describes how to write your own functions, called by the jBC language, in C code. 5 ICONV and OCONV Extensions shows the extensions you can make to the conversion codes in the ICONV and OCONV functions.. 6 The IOCTL Function describes the highly specialised IOCTL function. 7 jEDI Database Drivers describes how to write your own database driver so that jBC programs can seamlessly access data on alternate databases. 8 jEDI API Calls gives details of calling the jEDI API from a non-jBASE program. 9 Makefiles shows how to use the UNIX make program with jBASE applications. 10 jbc and BASIC describes in full the two compilation front-end programs, jbc and BASIC. 11 jBuildSLib and CATALOG describes in full the two execute creation front-end programs, jBuildSLib and CATALOG. 12 Advanced Tools describes more development tools that the advanced jBASE programmer may use. 1 Introduction

Advanced Programmers Reference 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.

arg | arg code or variable

viii Manual

Advanced Programmers Reference

Errata and Comments


If you have any comments regarding this manual or you wish to report any errors in the documentation, please send details to the address below:

Technical Publications Department JAC 599 Maxted Road Hemel Hempstead Hertfordshire HP2 7DX England

Tel:

+44 (0)1442 235 515

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.

Advanced Programmers Reference Manual

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

Requirements for Running jBASE


The basic requirement for jBASE is a standard UNIX system running on any hardware. This makes jBASE a truly Open Systems application development tool, with the resultant code being easily ported across many UNIX platforms. jBC applications will run in a UNIX SVR3.2 or later environment without difficulty. jBASE allows a user to take full advantage of the capabilities of UNIX System V Release 4 environment (SVR4.x). This offers many advantages over previous versions of the UNIX system. However, it is important to understand that jBC will produce applications of equal complexity in both environments.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

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.

ICONV and OCONV Extensions.


Two of the intrinsic functions supplied with the jBC language are the ICONV and OCONV functions. These provide general conversion of data between input and output formats. The type of conversion performed is defined during the call to ICONV or OCONV and jBC provides a large set of standard conversions. The user can extend this set of conversions. The OCONV Extensions chapter describes how this can be achieved.

The IOCTL Function


There is an intrinsic function of the jBC language called IOCTL. Its use is synonymous with the C ioctl function. It provides for miscellaneous control commands and queries to be sent and received to the database driver for an opened file. Its use is highly specialised and can vary between different database types.

jEDI Database Drivers.


A set of database types are supplied with jBASE. The database types include support for UNIX directories and for jBASE exclusive hashed files. There are circumstances when an application will want to perform database operations on databases other than those supported explicitly by jBASE. The chapter titled jEDI Database Drivers shows how a user can write their own database driver so that their applications can interface seamlessly with other databases.

Advanced Programmers Reference Manual

1-3

Introduction

jEDI API Calls


Although mostly used through jBC programs, jEDI is equally at home with non-jBC programs. The jEDI API Calls chapter describes how a C program can access the database functionality through calls to the jEDI library.

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.

jbc and BASIC Commands


The normal command used by a jBASE programmer to convert source code to compiled object is the BASIC command. However, the BASIC command is really just a front end to a more useful command called jbc. The chapter entitled jbc and BASIC shows the greater flexibility of the jbc command over BASIC, and also gives more complete information on the BASIC command.

jBuildSLib and CATALOG Command


The normal command used by a jBASE programmer to convert compiled object to an executable program or shared object (subroutine) is the CATALOG command. However, the CATALOG command is really just a front end to a more useful command called jBuildSLib. The chapter entitled jBuildSLib and CATALOG shows the greater flexibility of the jBuildSLib command over CATALOG, and also gives more complete information on the CATALOG command.

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

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

User Port Numbers


Some applications make use of port numbers in their application security routines and rely on the consistency of the port number to implement these systems. The user is automatically given a unique port number at login time. However, networked and terminal server based systems may cause a different port number to be assigned for each login session. This can cause a problem for port based security routines. The consistency of the port number can be guaranteed in jBASE by setting the environment variable, JBCPORTNO, to a unique port number according to the name of the user who logged in. This can be achieved by executing the shell command below. JBCPORTNO=UseridBasedPortNo ; export JBCPORTNO

Migration Manual

2-6

Advanced Programmers Reference

Generating an Application Account


The following instructions assume that jBASE has been installed and configured on the target UNIX system. Once a user id has been allocated for the application account, the application id should be created by the standard UNIX sysadm, or equivalent utilities and the Korn shell (ksh) should be selected as the default login shell. The application id should be used to restore the application files and perform the migration. This will ensure that all the files are created with the correct user and group permissions. The next step is to configure the application account or home directory for jBASE. The easiest way to configure the application home directory for jBASE is to execute the IJU command. The IJU command will create or modify the home directory .profile script inserting shell commands to set-up various environment variables. To execute the IJU command enter the following shell commands: $ PATH=/usr/jbc/bin:$PATH This command inserts the absolute jBASE command path into the current command path. This means that the jBASE command path will be searched before any other paths when looking for the command to be executed. $ LD_LIBRARY_PATH=/usr/jbc/lib This command defines the absolute jBASE shared library path. This environment variable is used by the dynamic linker to find jBASE shared library functions. The dynamic linker will default to system library paths when the paths in the environment variable have been exhausted. $ export PATH LD_LIBRARY_PATH The export command adds the new environment variables to the list of environment variables to be exported when a command is executed. $ IJU The IJU command will prompt the user with a series of simple questions on how to configure the home directory .profile script file. The default .profile file, which is allocated by the UNIX system when a new home directory is created, can be changed to include a default set of jBASE environment variables. The file location of the system default .profile file can vary however, the location should be found by consulting the system documentation. Once created, any additional environment assignment or processing can be included - although care should be taken to ensure that any system or jBASE environment variables are set-up before the execution of the jbcconnect command. This command performs internal jBASE initialisation. The easiest method of executing the modifications made to the .profile script is to terminate the current login shell process and then login again making sure that the application id is used. To restore the application source account, login to the application directory using the application user id. Then attach the media device by executing the following command: T-ATT SCT The device name will depend upon the devices available in the /usr/jbc/de file. The above example uses the Streaming Cartridge Tape mnemonic, SCT. Before restoring the application source account, the buffer or frame size of the current system should be considered. The reason for this is that the restore command can be instructed to modify the file modulo as the source files are recreated in the applications home directory. The format of the jBASE restore command is detailed below. One of the blocking options should be selected, depending on the frame size of the source machine. This command assumes that the original file modulo has been sized correctly for the existing system or that the relevant reallocation parameters have been configured. Blocking options: -b8 -b4 -b2 use this option if the source machine uses 1/2k frame size. use this option if the source machine uses 1k frame size. use this option if the source machine uses 2k frame size.

% ACCOUNT-RESTORE {options} applicationid

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

Chapter 3: Execute Paths

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.

Advanced Programmers Reference Manual

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

Execute Paths Manual

3-2

Advanced Programmers Reference

Compiling Object Code


The general mechanism for compiling jBC code to object is the BASIC command. The BASIC command will take a source record from a jBC file and create an object record in the same file. The file type can be any type supported by jEDI - a hashed file, or a UNIX directory for example. The name of the output record can be in one of two formats. If the input record key ends in .b, the output record key will have the same prefix but with a .o suffix. If the input record key does not end in .b, the output record key will be the input record key prefixed with a $ character. Example Create a main program and a subroutine, and compile them. % % % % mkdir src vi src/mainprog vi src/sub1 BASIC src mainprog sub1

Advanced Programmers Reference Manual

3-3

Execute Paths

Creating a UNIX Executable


The general mechanism for converting object code to an executable program is the CATALOG command. The CATALOG command will take a source record from a jBC file and will either create an executable program or a shared object function. The only input record keys used by the CATALOG command are those with an extension of .o or a prefix of $. If the input record was originally a SUBROUTINE, it will create shared object in the directory specified by the expression $HOME/lib. If the input record was not a SUBROUTINE, it will create a UNIX executable in the directory given by the expression $HOME/bin. Example Create a main program and a subroutine, compile them and create executables. % % % % % mkdir src vi src/mainprog vi src/sub1 BASIC src mainprog sub1 CATALOG src mainprog sub1

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.

Execute Paths Manual

3-4

Advanced Programmers Reference

Execute the Command


Once the program and subroutine have been created, you can execute the program from any of the UNIX shells, from the jBASE shell (jsh), from a UNIX script, from a jBASE script, from jBASE program and so on. In the example shown in Creating a UNIX Executable above, the executable will have been placed in directory $HOME/bin. Assuming that this directory is part of your PATH environment variable, there are a number of ways of executing it. Example 1 Execute from a shell. % mainprog Example 2 Execute from another jBC program. % COPY src a.n.other.b \(T a.n.other.b 001 EXECUTE mainprog Example 3 Use an explicit path name % $HOME/bin/mainprog

Advanced Programmers Reference Manual

3-5

Execute Paths

Program Execute Paths


Once an application source has been turned into a UNIX executable program (using BASIC/CATALOG/jbc), you have to think about how to access the program. By default, the CATALOG command puts the executable programs into the directory $HOME/bin and so by default you should have $HOME/bin somewhere in your PATH environment variable. Supplied with jBASE is a script called IJU (Install jBASE User ) that allows the systems administrator to create a template .profile script for a user. Happily, the .profile created using IJU will include $HOME/bin as part of the users executable path. As the jBASE programs created are pure UNIX programs, they can be placed anywhere on your UNIX system. Then, to execute them, you can either use a full path name (such as /home2/pipe/slipsource/fired) or, assuming that the directory is part of the PATH environment variable, just the program name. As with all UNIX programs, the order of directories in the PATH environment variable is important. UNIX will attempt to load programs in left to right order of the directory names in the PATH variable. There is an exception to this rule, which happens when you attempt to execute a jBASE program from another jBASE program (or from a jCL script). If you have a Master Dictionary (or MD for short) file defined using the JEDIFILENAME_MD environment variable, jBASE will first look in this file to see if the program to be executed is a jCL script. This takes precedence over external UNIX programs found in the PATH of the user.

Execute Paths Manual

3-6

Advanced Programmers Reference

Subroutine Execute Paths


When a source is a subroutine rather than a program, the CATALOG command will created a shared object rather than an executable program. The default for CATALOG is to place the result in the directory $HOME/lib, although this can be over-ridden - see the jBuildSLib and CATALOG chapter. Subroutines are executed through the CALL statement in a jBC program. When the first CALL statement is executed, the library code will perform the following initialisations: Find details of all shared objects in the components given by the expression $JBCOBJECTLIST. The JBCOBJECTLIST environment variable is a list of UNIX directories (which must be absolute path names) or individual shared objects. Each component in the list delimited by a : character. If the environment variable JBCOBJECTLIST has not been established, find details of all shared objects in the directory $HOME/lib. Find details of all shared objects in the directory $JBCRELEASEDIR/lib. If the environment variable JBCRELEASEDIR has not been established, find details of all shared objects in the directory /usr/jbc/lib. Should a duplicate subroutine name be found while searching the different shared objects, the first subroutine will be given precedence and subsequent duplicate subroutine names will be ignored. A shared object has two components. The first is the actual object, which will have a .so suffix. The second is a description of the exported symbols in the shared object, and this will have a .el suffix. Example Assume the environment variable JBCOBJECTLIST is set up as: % export JBCOBJECTLIST= /home2/pipe/lib/libtest.el:/home2/slip/release and that environment variable JBCRELEASEDIR is set up as: % export JBCRELEASEDIR=/usr/jbcdev When the first CALL statement is executed, the library code will: Open file /home2/pipe/lib/libtest.el and parse the file. Open directory /home2/slip/release and parse for all files with a .el suffix. Open directory /usr/jbcdev and parse all files with a .el suffix. The exception to this are the shared objects that either have already been parsed, or the shared objects that are currently in use by the jBC run-time library. The parsing of the .el files tells the library code about the shared object. There are two formats of an .el file. The first format of an .el file is that created by the jBuildSLib command. In this case, the file might look something like this: % COPY . apps.com.el \(T apps.com.el 001 JBC_SUB1 002 JBC_DOTHIS 003 JBC_DOTHAT This tells the library code that a file named apps.com.so exists and that it contains three jBC subroutines, SUB1, DOTHIS and DOTHAT. The second format of a .el file is that created by the CATALOG command. In this case, the file might look something like this: % COPY . libpipeman.el \(T libpipeman.el 001 JBC_SUB1\JBC_DOTHIS]JBC_SUB2\JBC_DOTHAT 002 libpipeman0.so]libpipeman1.so 003 SUB1.o\DOTHIS.o]SUB2.o\DOTHAT.o

Advanced Programmers Reference Manual

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.

Execute Paths Manual

3-8

Advanced Programmers Reference

Copying Programs and Subroutines


The above descriptions give an insight into how jBASE uses the PATH variable for finding executable programs and the JBCOBJECTLIST variable for finding subroutines. By default, the CATALOG command will place programs in the directory $HOME/bin, and subroutines in directory $HOME/lib. All the user needs to do is to set the PATH variable to include $HOME/bin and you can execute programs that can call subroutines. Note the JBCOBJECTLIST need not be set in this case, as it will default to $HOME/lib. The following examples show how to copy your programs and subroutines around the system to make them accessible to other users. Example 1 Make program accessible to others. It is very easy to allow other users to access the programs you have created in a directory. Assuming the program is /home/pipex/bin/countslip, the user simply has to do one of the following: Put the directory /home/pipex/bin into their HOME environment variable, for example: % export PATH=$PATH:/home/pipex/bin % countslip Call the program using an absolute path name, for example: % /home/pipex/bin/countslip Note that UNIX permissions apply to these programs. Example 2 Copy a program to a common live directory. This example shows how a program can be copied from say, a development account to a live account. To copy program countslip from the account dev (which we assume the user is logged on to) to the account d4live (which the live users log on to), you would do something like this: % cp $HOME/bin/countslip ~d4live/bin Before you make the copy, you must to consider if any users are currently using the live program countslip, or are about to use it. You can check for current usage with the WHERE command, for example: % WHERE | grep countslip Depending upon the implementation of UNIX, if you copy over an active program, either the copy will fail, or the copy will succeed but the user running the original copy will probably get a segmentation violation. You can ensure you dont have any segmentation violations by one of two methods: Remove the original live program before copying the new one. This will works but means that the live application may fail if it tries to load the program after the old one is removed and before the new one is copied. Note that removing a program does not affect any users currently using it. For example: % rm ~d4live/bin/countslip % cp $HOME/bin/countslip ~d4live/bin Inhibit logons to jBASE using the INHIBIT-LOGONS command, log off all current users, perform the copy and then allow users to log back on by using the ENABLE-LOGONS command. The following example assumes you are root user: % % % % % % % INHIBIT-LOGONS \* MSG \* Please log off in the next 60 seconds ... SLEEP 60 WHERE LOGOFF nnnn # repeat for all users logged on cp $HOME/bin/countslip ~d4live/bin ENABLE-LOGONS \*

Advanced Programmers Reference Manual

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:

Execute Paths Manual

3-10

Advanced Programmers Reference

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

Advanced Programmers Reference Manual

3-11

Execute Paths

Chapter 4: C Extensions

Calling C Functions - Overview


The jBC language provides a number of mechanisms for extending the language. The three main mechanisms for achieving this are: 1. 2. 3. Calling external functions written in the C language. Extending the scope of the OCONV() and ICONV() functions. Extending the scope of user-exits, used mainly in legacy applications.

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.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

Fundamental Components of C Interfaces


jBC Supplied Header File
A header file is supplied with jBASE for inclusion in C functions. The file is named jsystem.h and resides in the directory $JBCRELEASEDIR/include. If you compile the C source using the cc command you will need to include this directory in the include path, for example: % cc func1.c -c -I $JBCRELEASEDIR/include If you use the jbc command instead, the directory is automatically included and your command is simply: % jbc func1.c -c The jsystem.h header file contains all the definitions required to support C function calls from a jBC program, including definitions for VAR, INT32, FLOAT and STRING as detailed below. Your C source would normally include this file at the beginning. For example: #include <jsystem.h> INT32 MyFunc(FLOAT Param1) { /* */ } Note that the jsystem.h file also includes the standard C header file stdio.h, as well as other jBASE provided headers.

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.

Advanced Programmers Reference Manual

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.

FLOAT A floating point number using jBC internal representation.


C Extensions Manual 4-4 Advanced Programmers Reference

Automatic Type Conversion


The variables in a jBC program are typeless. This means that the programmer doesnt have to be concerned with the format of the variable. Consider the following: Var1 Var2 Var3 Var4 = = = = 12 12.34 12.34 Var1 + Var2 + Var3

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.

C Function Type Conversion


As described above, the compiler will cater for all the type conversions required for the parameters and return value of the function. Within the C function there may be times when type conversion is required. Supplied with jBASE in the header file jsystem.h are a number of defined macros and functions to support this. These conversions are mostly used when a VAR type parameter is used. In other words we use the address of the jBC variable which at this point is typeless. Consider the following example of jBC code and C code: DEFC MyFunc(VAR) PRINT Enter details : : INPUT Detail PRINT This result of MyFunc is :MyFunc(Detail) #include <jsystem.h> INT32 MyFunc( VAR * param1) { /* ??? */ return 123; } When the function MyFunc is called, the parameter passed is the address of the variable Detail. At this point no type conversion has occurred. When function MyFunc wants to examine the variable passed as the first parameter (in the C variable param1), it will usually want to force it to be a certain type. If it needs to be of type INT32, the function could do this: INT32 I1 ; I1 = CONV_IB(param1); If it needs to be a 0 (null) terminated string, it could do this: STRING * S1 ; S1 = CONV_SFB(param1); A full list of conversion functions and macros are provided later in this chapter.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

A More Complex Example


This example allows the user to enter a string of characters and a number, call function ParseIt and then print the result of function ParseIt. The C function ParseIt will extract a number of space delimited fields, and return just that number of fields. First, the jBC source which we will assume to be in file prog2.b: DEFC VAR ParseIt(INT, VAR ) PRINT "Enter sting to parse : ": INPUT StringIn PRINT "Enter number of fields to parse : ": INPUT FieldQty PRINT "Result = " : ParseIt(FieldQty, StringIn) Now the C source which we will assume to be in file prog2C.c: /* * Include the header definitions. */ #include <jsystem.h> /* * Start off the function definition. */ VAR *ParseIt(VAR *result,INT32 qty,VAR *string_in) { /* * Declare the automatic C variables we require. */ STRING * start, * end ; /* * Make sure input is a 0 terminated string. */ start = end = CONV_SFB(string_in) ; /* * Scan through 'qty' occurrences of * space characters. */ while(*end != '\0' && qty > 0) { if (*end++ == ' ') qty--; } /* * Store all characters between the start and end. */ STRING_MAKE_NEW_VBIS(result,(end-start),start); /* * Return correct address to caller. */ return result ; } The two sources can be compiled to create an executable program with: % jbc prog2.b prog2C.c -o prog2 Notes: Because the return type is VAR, the first parameter passed to ParseIt will be the address of a variable allocated by the compiler. The function ParseIt uses this as its return variable, and upon return must always provide this as the return parameter. The macro STRING_MAKE_NEW_VBIS will create a variable of type STRING given the length and address of the string to create. This macro is described in more detail later.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

Register v. User Variables.


There are two main types of jBC variables, one for user variables and the other for register variables. Typically, user variables are created by the user program, just by virtue of declaring them. Register variables are temporary scratch variables, used repeatedly, and are allocated by the compiler. For example, the following jBC source code will contain 13 user variables (12 for Array and 1 for Var1), plus 1 register variable in order to contain the result of the concatenation.: DIM Array(3,4) Var1 = This is Var1 MAT Array = Var1 : plus the rest Under certain circumstances, an internal jBC function, called from a user-written C function, will destroy the contents of register variables. Consider the following jBC source: DEFC MyFunc(VAR,VAR) PRINT Enter parameter V1 : : INPUT V1 PRINT Result of MyFunc = :MyFunc(V1 : xyz, abc) The compiler will cause the concatenation of variable V1 and the string constant xyz, and place the result in a register variable allocated by the compiler. Now consider the C function to support this: #include <jsystem.h> INT32 MyFunc ( VAR * param1,VAR * param2) { INT32 I1 ; I1 = INDEX_IBBI(param1, param2, 2 ); I1 += DCOUNT_IBB(param1, param2) ; return I1 ; } In this example, the jBC function INDEX_IBBI will recognise that the parameter param1 as a register variable, and so the contents of the variable will be destroyed. Thus, the call to DCOUNT_IBB is passed an invalid variable (param1). This destruction of register variables is a feature to enable jBC to consume smaller amounts of memory than would otherwise be possible. As you will see later, it can be turned to your advantage to improve performance. Should you believe that you are going to encounter this situation, you will need to make a temporary copy of the passed parameter, as discussed below.

Advanced Programmers Reference Manual

4-9

C Extensions

Creation and Destruction of VAR Type Variables


Within a C function there may be a need to create VAR type variables as part of the function. There are a number of macros and functions to enable the variable to be created and destroyed. The variable first needs to be declared within you C function like this: #include <jsystem.h> INT32 MyFunc(INT32 param1) { VAR WorkVar, DoThis ; Before using the variable, you must initialise the elements of the structure. You can do this with one of two macros, the first to create a register variable, and the second to create a user variable. You will most likely use the second and create a user variable. For example: #include <jsystem.h> INT32 MyFunc(INT32 param1) { VAR WorkVar, DoThis ; STRING_INITIALISE_REG_VB(&WorkVar); STRING_INITIALISE_USER_VB(&DoThis); In the above example the variable WorkVar is initialised to be a register variable, and the variable DoThis is initialised to be a user variable. Once initialised from a C point of view, they are now the equivalent of uninitialised variable in the jBC language. Therefore, before they can be referenced they need to have data added to them. For example, you might do this: STORE_VBI(&WorkVar, 123) ; STORE_VBS(&DoThis, (STRING*) Value of WorkVar ); CRT_VB(&DoThis); CRTN_VB(&WorkVar); Finally, at the end of the program, any storage space used by the variable needs to be released (or if a file descriptor, the file needs to be closed). The macro to release all space and close files is STRING_RELEASE_EXT_VB, for example: STRING_RELEASE_EXT_VB(&WorkVar) ; STRING_RELEASE_EXT_VB(&DoThis) ;

C Extensions Manual

4-10

Advanced Programmers Reference

Supported Functions and Macros


Within the supplied header file jsystem.h there are a large number of functions and macros to enable you to manipulate VAR type variables. The functions and macros explicitly supported are detailed here. You can also use the macros generated by the jBC compiler, such as DCOUNT_IBB. While these are not explicitly supported and are subject to change, the nature of their usage means that it is highly unlikely they will change. All the defined functions and macros have the name format NAME_rpppp. The first part of the name NAME describes the action, such as COUNT or INDEX. The second part of the name describes the return value and the passed parameters. For example, the macro INDEX_IBBI describes the INDEX function, the return value is I for INT32, and there are 3 passed parameters, B for BASIC variable address, B for BASIC variable address and I for INT32. There are often two forms of the macro or function you will see defined in jsystem.h. For example JLibECOUNT_IBB and COUNT_IBB. Always use the second form. The supported functions and macros are defined in the following paragraphs.

Macros to Store Values into a Variable.


STORE_BBF(Target,Numeric) Store a FLOAT type variable into a VAR variable, and return the address of the Target VAR variable. Consider a function that takes two floating point values, multiplies them and returns the result. The jBC source would look like this: DEFC VAR Mul2(FLOAT, FLOAT) INPUT float1 INPUT float2 res = Mul2(float1,float2) PRINT The result is :res The C function source would look like this: #include <jsystem.h> VAR * Mul2(VAR * result, FLOAT value1, FLOAT value2) { return STORE_BBF(result, MUL_FFF(value1, value2)); } STORE_VBF(Target,Numeric) Same as STORE_BBF, except a void is returned. STORE_BBB(Target,Source) Make an identical copy of a VAR variable, and return the address of the Target VAR variable. The Source variable can be of any type, for example integer, float, file descriptor, select list and so on. If Source is the same as Target, no operation is performed. If the Source variable is a register variable, the contents of Source are deleted and become indeterminate. This is a performance feature - what actually happens is that instead of the Source variable being copied, just the pointers are copied and the operation is substantially quicker. STORE_VBB(Target,Source) Same as STORE_BBB except a void is returned. STORE_BBI(Target, Numeric) Store an INT32 type variable into a VAR variable, and return the address of the Target VAR variable. Consider a function that takes two integer values, performs a logical XOR on them and returns the result. The jBC source would look like this:

Advanced Programmers Reference Manual

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.

Variable Type Conversion


CONV_FI(Integer) Given a 32 bit integer, convert it to a FLOAT. For example, could be used to multiply a FLOAT with an INT32 as follows: FLOAT result, value1 ; INT32 int1 ; /* Set up value1 and int1 */ result = MUL_FFF(value1, CONV_FI(int1)) ; CONV_IF(Float) Given a FLOAT return a 32 bit integer.

C Extensions Manual

4-12

Advanced Programmers Reference

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

Advanced Programmers Reference Manual

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.

VAR Variable Creation and Destruction


STRING_INITIALISE_REG_VB(Variable) Initialise the elements of a VAR variable so that it looks like an uninitialised register variable to other jBC functions. This must always be used on a variable before attempting to modify that variable. A typical example is: INT32 MyFunc() { VAR Var ; STRING_INITIALISE_REG_VB(&Var); STRING_INITIALISE_USER_VB(Variable) Initialise the elements of a VAR variable so that it looks like an uninitialised user variable to other jBC functions. STRING_RELEASE_EXT_VB(Source) All the external resources associated with the variable Source are released. For example, if it were an opened file descriptor then the file will be released. If it were a STRING type variable, then the storage space associated with it will be released, even if it is a 0 length string. STRING_RELEASE_REG_VB(Source) Similar to STRING_RELEASE_EXT_VB except that small STRINGs are not released. This function is useful where variables are going to be re-used more than once, and can bring performance benefits by not repeatedly allocating and freeing small strings. You should not use it for automatic variables declared in the C function. Consider the following example: INT32 MyFunc() { VAR Var ; STRING_INITIALISE_REG_VB(&Var) ; STORE_VBS(&Var, (STRING*) 1234567890);
C Extensions Manual 4-14 Advanced Programmers Reference

/* 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; }

STRING Type Manipulation


STRING_RESIZE_VBI(Target, Length) This will re-size the variable Target of type STRING to become length Length in size. The original contents will be preserved. This is useful, for example, to make a variable larger to add some extra information at the end. It is also useful to truncate the current contents of a variable. If the variable Target is not already of type STRING, the content of the variable will be lost, or if a file descriptor, the file will be closed. If you do not want to retain the original contents of the variable, use the STRING_MAKE_NEW_VBIS functionality - keeping the contents is a potential overhead you do not need. STRING_MAKE_NEW_VBIS (Target, Length, Source) This will make the variable Target into a type STRING variable of length Length bytes. The contents as pointed to by Source will then be copied to the newly allocated STRING. If the variable Target is not already of type STRING, the contents of the variable will be lost, or if a file descriptor, the file will be closed. If you want to retain the original contents of the variable, use the STRING_RESIZE_VBI functionality. The parameter Source can optionally be a NULL pointer, in which case the STRING is still created but the contents of it are indeterminate.

Advanced Programmers Reference Manual

4-15

C Extensions

Replacing SUBROUTINEs with C Functions


So far we have shown how a C function can be called by a jBC program. However, you can use similar rules to replace a SUBROUTINE written in jBC with a SUBROUTINE written in C. Consider the jBC source to call a subroutine, for example: Var1 = This is variable Var1 Var2 = 123 Var3 = CALL MySub(Parameter1, 2, Var1 : Var2, Var3 ) PRINT The new value of Var3 = : Var3 The first parameter, Parameter1, will be copied to a register variable allocated by the compiler. The second parameter, 2, will be stored in another register variable allocated by the compiler. The third parameter will be calculated at run-time and stored in a third register variable allocated by the compiler. The fourth parameter, Var3, will be passed as it is, in other words the subroutine will see the address of a user variable. The jBC source for this SUBROUTINE may look like this: SUBROUTINE MySub(p1, p2, p3, p4 ) p3 = p1 : p2 : p3 RETURN Note that only p4 can be modified. The SUBROUTINE could choose to use and modify the other parameters, but as they were stored in temporary register variables the calling jBC program would not be able to reference the changes. The jBC source above for the SUBROUTINE can be replaced with C code. To get the template of the C code, try the following (assuming the jBC source code is in file myfunc.b): jbc -S myfunc.b This generates the template files myfunc.c and myfunc.b. You can then modify them as you wish, and maintain the SUBROUTINE in C from then on. After stripping out un-necessary comments from myfunc.c and mufunc.j, and merging into a single file, you will be left with something like this: #define InternalFunc 1 #include <jsystem.h> static char * VarFiles[] = { "myfunc.b", NULL }; static char CreateDate[] = "jBC version 3.0 Tue Apr 25 14:20:13 1995"; static struct VARLocalString string[] = { VAR_TYPE_STRING, VAR_FLAGS_CONSTANT, 3, "JAC", NULL, 0, 0, NULL, NULL, NULL}; #define DEF_SEL_VAR &GlobalBasicVars[0] #define DEF_FILE_VAR &GlobalBasicVars[1] #define NUM_VARS 2 #define NUM_REGS 1 #define _R &(((struct {VAR _jb_[1];}*)&GlobalBasicVars[2]))->_jb_ static struct StaticLevelData JBCLevel = {0000, 4, 10000.0, NULL, VarFiles, NUM_VARS, NUM_REGS, "SUBROUTINE MyFunc", CreateDate, NULL } ; #undef MAIN_START #undef MAIN_INIT #define MAIN_START #define MAIN_INIT(NullDeclaration) #define MAIN_INIT_COMMON #define SUBROUTINE_INIT_COMMON MAIN_START MAIN_INIT(NULL) MAIN_INIT_COMMON JBC_MyFunc(ActualFlags, _jb_p1, _jb_p2, _jb_p3, _jb_p4) char * ActualFlags; VAR * _jb_p1;
C Extensions Manual 4-16 Advanced Programmers Reference

VAR VAR VAR {

* _jb_p2; * _jb_p3; * _jb_p4;

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.

Advanced Programmers Reference Manual

4-17

C Extensions

Compiling and Linking with C Functions.


There are a number of ways that a jBC source can be compiled and linked with C functions to create an executable program. In practice, many of the examples below would be performed using make files, rather than the developer typing the commands in at the shell prompt. Compiling sources together. For example, to compile a jBC source with the two C functions it requires: % jbc prog1.b func1.c func2.c -o prog1 Compiling sources with objects. Separately compile the jBC source and the C source: % % % % jbc jbc jbc jbc -c prog1.b -c func1.c -c func2.c prog1.o func[12].o -o prog1

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

Advanced Programmers Reference

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

Advanced Programmers Reference Manual

4-19

C Extensions

General Rules and Helpful Hints


This section gives a list of useful rules and hints to apply to your C code. Always use the supported functions, macros and definitions. In the future, the internal workings of jBASE may change, but by using supported functionality, at the very worst a re-compilation will be necessary. Try to make your C code portable, even if you believe the code will only ever run on a single architecture. Many books have been written on this issue, and some examples were described earlier in this chapter. Try to build your C functions into shared objects where possible. Make the most of make files to build your code. Remember that when the C function is called, some of the parameters may point to the same variable. Consider the following jBC source: DEFC myfunc(VAR, VAR ) Var1 = This is Var1 myfunc(Var1, Var1 ) The C code for myfunc would look something like this: #include <jsystem.h> INT32 myfunc(VAR * p1, VAR * p2) { The variables p1 and p2 would point to the same variable. If you modified the variable at p1, it would affect the data pointed to by p2. You should be careful of register variables. These can be created using the STRING_INITIALISE_REG_VB macro described earlier, or more likely passed to your C function by the compiler. If you call some of the jBC functions such as DCOUNT_IBB, they will potentially release any external space associated with a register variable. This can be used to your advantage in some cases. Consider the following C function that simply appends the string xyz to the parameter p1 and places the result in parameter p2: #include <jsystem.h> INT32 cat(VAR * p1, VAR * p2) { STORE_VBB(p2, p1 ); STRING_RESIZE_VBI(p2, CONVLEN_IB(p2)+3); memcpy(CONV_SB(p2)+CONVLEN_IB(p2)-3,"xyz",3); return 0; } If the above C function were called from the following jBC source: DEFC cat(VAR, VAR) p1 = "this is" cat(p1:" from here", p2) parameter p1 in the C function will point to a register variable. This will mean the subsequent STORE_VBB executes much quicker, as it recognises the source variable is a register variable, and so just copies pointers and voids the source, rather than actually copying the data. This subject is also discussed in the earlier description of STORE_VBB. Do not use the chdir() function call directly. This is because, for jEDI functionality, we always need to know the current working directory, and for performance reasons we cannot continually call getcwd(). Instead, if you want to change the current directory, use the jBC macro CHDIR_IB. The following is an example of changing the current working directory to /home2, and returning 1 if it succeeded or returning 0 on error (although as an example, it would be easier for the calling jBC code to use the intrinsic CHDIR() function). #include <jsystem.h> INT32 ChdirToHome2() { VAR WorkVar ; int rc ;

C Extensions Manual

4-20

Advanced Programmers Reference

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);

Advanced Programmers Reference Manual

4-21

C Extensions

Chapter 5: ICONV and OCONV 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.

Advanced Programmers Reference Manual

5-1

OCONV Extensions

Conversion Code Extensions


As an example, assume you want to support the conversion code BF with an OCONV or ICONV function. The calling jBC source may look like this: PRINT OCONV(I am on a diet, BF) Under normal circumstances this would fail and the debugger would be entered with an illegal conversion code message. You can write an extension to the conversion codes by creating a normal SUBROUTINE with the name JBCUserConversions with five parameters. An example of source code to handle the BF conversion code extension is: SUBROUTINE JBCUserConversions( result, source, code, type, error) BEGIN CASE CASE code = "BF" IF type THEN result = source : " was OCONV" END ELSE result = source : " was ICONV" END CASE 1 error = 1 END CASE RETURN Use the BASIC and CATALOG commands to make the subroutine available, and call it using the normal SUBROUTINE calling mechanism. The five parameters passed to the SUBROUTINE are: result This is the variable to be updated with the conversion.

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.

OCONV Extensions Manual

5-2

Advanced Programmers Reference

User Exit Code Extensions


As explained previously, the ability to write assembler code and call the assembler code as a user-exit is provided purely for legacy applications. This technique is not recommended for new applications. A user-exit can be called by a user application via the U conversion code in an ICONV or an OCONV function. For example, the jBC code might be: user = OCONV(,U50BB) In this example, the user-exit 50BB is a historic user-exit and is already supplied by the jBASE development system (it returns the port number and name of the user). In the above example, the value 50BB is a description of the user exit number and is split into two components. The first character is historically called the entry-point, and in this case the entry-point is 5. The next 3 characters are historically called the frame-id which in this case is 0BB. The user-exit number is a 1 to 4 digit hex value, case insensitive. If fewer than 4 characters are used, it is padded with leading zeroes. For example, user-exit U23 means U0023, or entry point 0 in frame-id 023. Let us consider the example below where we call user-exit 90fb: res = OCONV(input details, U90fb) Under normal circumstances this would fail and the debugger would be entered with an illegal conversion code message. You can write extensions to the conversion codes by creating a normal SUBROUTINE with the name Uxxx for each of the frame-ids you wish to support. There are 5 parameters passed to a subroutine, as for the Conversion Code extensions described earlier. A simple example of such a source code to handle just the U90fb user-exit is: SUBROUTINE U0FB( result, source, code, type, error) UCASE = OCONV(code, MCU)[1,5] BEGIN CASE CASE UCASE = "U90FB IF type THEN result = source : " was OCONV" END ELSE result = source : " was ICONV" END CASE 1 error = 1 END CASE RETURN The SUBROUTINE is functionally very similar to that for the Conversion Code Extensions. There main difference is the line: UCASE = OCONV(code, MCU)[1,5] This is to make the conversion code quicker and more reliable to parse. For example, the user exit U90fb could take the following formats: PRINT OCONV(,U90fb) PRINT OCONV(,u90FB) PRINT OCONV(,U90Fb : CHAR(253) : Extra detail )

Advanced Programmers Reference Manual

5-3

OCONV Extensions

Chapter 6: IOCTL Function

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.

Advanced Programmers Reference Manual

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.

IOCTL Function Manual

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)

Advanced Programmers Reference Manual

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

IOCTL Function Manual

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

Advanced Programmers Reference Manual

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

IOCTL Function Manual

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

Advanced Programmers Reference Manual

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

IOCTL Function Manual

6-8

Advanced

Programmers

Reference

Chapter 7: jEDI Database Drivers

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.

Advanced Programmers Reference Manual

7-1

jEDI Database Drivers

Shared Object Database Drivers


A shared object database driver is the mechanism by which a program using the jEDI API to access a file (such as a jBC program) can use a database driver, contained in a standard shared object, to provide the necessary database I/O functionality. The application that is calling the jEDI interfaces will remain unaware of where exactly the data is coming from, or from what sort of database the data is contained in. There are six steps to creating a shared object database driver. An example of a shared object database driver is supplied with jBASE in the source file $JBCRELEASEDIR/src/jediDExample.c. It may also be useful to examine the supplied source $JBCRELEASEDIR/src/jediCExample.c, which shows an example of a C program using calls to jEDI to perform database operations. The following short example is a useful introduction to building a shared object database driver. It is recommended the reader attempts this example first of all. Step 1 The first step involves creating an external database driver source. This of course is the hardest step and will be expanded upon in much more detail later on. For now though, we will use the supplied example source code. Use the following steps at shell: % % % % cd ;# Change to home directory mkdir sosrc ;# Create a source directory cd sosrc ;# Change to the new source directory cp $JBCRELEASEDIR/src/jediDExample.c ./mydd.c ;# Copy source to file mydd.c

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

jEDI Database Drivers Manual

7-2

Advanced Programmers Reference

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

Advanced Programmers Reference Manual

7-3

jEDI Database Drivers

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.

jEDI Database Drivers Manual

7-4

Advanced Programmers Reference

File Ajar Processing


There is a limit to the number of files that a process can have open at any one time. Depending upon the operating system and its configuration, this is often in the region of 50 to 200. Unfortunately, this clashes with many applications that require hundreds of files open simultaneously. The mechanism to work around this is called file ajar processing. This involves temporarily closing the file to the operating system, but the application still believes the file is opened. Then, when a database operation is requested on a file that is temporary closed (or ajar), a different file is closed and made ajar, and the requested file is re-opened. This mechanism works on a least used file algorithm so that the most frequently used files do not suffer the performance penalty. This ability to temporarily close and reopen files depends upon the database driver itself, and a database driver can choose to implement it or not. The jEDI API provides this functionality itself, but uses two extra functions supplied by the database driver to accommodate this. During the OPEN function call, detailed later, there are two members to the file descriptor called ProcessAjar and ProcessReopen. By default these members are initialised to NULL and the database driver will not support ajar file processing. If you want to support file ajar processing, these two members need to be set to the address of two support functions within the database driver to handle the following functionality.

File Descriptor Member ProcessAjar


Set the member ProcessAjar in the file descriptor (see the OPEN function description later) to the address of a database driver support function that will be called by the jEDI API each time a file is required to be temporarily closed. The description of this function supplied by the database driver is: Synopsis: static void MakeFileAjar( 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: None. 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->ProcessAjar = MakeFileAjar ; Hence, the actual name of the function is not important as jEDI will do the call through a function pointer. This function should perform all necessary functionality to temporarily close the file and return. It is assumed the function always succeeds. If it fails, no error is reported and jEDI will try to make another file 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 close these automatically. Remember that this type of functionality should only be supported by the database driver if closing the files doesnt cause any harm, such as losing any UNIX file locks that were set.

File Descriptor Member ProcessReopen


Set the member ProcessReopen in the file descriptor (see the OPEN function description later) to the address of a database driver support function that will be called by the jEDI API each time a file is that has previously been temporarily closed needs to be re-opened. The description of this function is:

Advanced Programmers Reference Manual

7-5

jEDI Database Drivers

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.

jEDI Database Drivers Manual

7-6

Advanced Programmers Reference

jEDI Support Functions.


This section describes some of the support functions available to the database driver. In particular, note the mandatory use of JediMalloc() instead of the normal malloc() function, and similar memory allocation routines.

Memory Allocation Functions


When an application connects to jEDI using function JediConnect (see the chapter jEDI API Calls) it provides a list of memory allocation routines for use by the database driver. This will normally be the standard functions such as malloc(), free() and so on, but there are specialised circumstances when this will not be the case. A database driver should not therefore use the memory allocation functions such as malloc() or realloc(). The driver should instead use a supplied alternative set, where the first parameter passed is the file descriptor variable that was passed to the database driver. The calls to use are: void * JediMalloc(JediFileDescriptor * fd, int size); void * JediRealloc(JediFileDescriptor * fd, void * source, int newsize ); void * JediCalloc(JediFileDescriptor * fd, int qty, int size ); void * JediStrdup(JediFileDescriptor * fd, void * source ) ; void JediFree(JediFileDescriptor * fd, void * source ) ; void * JediReadMalloc(JediFileDesciptor * fd, int size); The functions JediMalloc, JediRealloc, JediCalloc, JediStrdup and JediFree are the replacement functions to be used by the database driver instead of malloc, realloc, calloc, strdup and free respectively. The function JediReadMalloc is to be used by the READ function in the database driver when it is re-sizing a buffer that was originally supplied. The use of JediReadMalloc is detailed later in the READ function description.

Record Locking Function


The database driver has to supply a LOCK function so that record locking functionality can be provided. The database driver can choose to do whatever it likes with these requests. However, the database driver may wish to make calls to the same locking function as used by jBASE internal database drivers. int JediSystemLock(JediFileDescriptor * fd, int command, int hash ) ; fd is the file descriptor that was passed to the LOCK function. The following members of the file descriptor must be initialised (usually done by the database driver in the OPEN code): FileId1 and FileId2 FileFd Two integers that uniquely describe the file. See the description of the OPEN function. The UNIX file descriptor of the opened file. If the jRLA demon is active, then this entry is ignored. Otherwise, we use UNIX locks against the file descriptor in this entry.

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.

Advanced Programmers Reference Manual

7-7

jEDI Database Drivers

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.

jEDI Base Code


There is some functionality provided by jEDI for all database drivers which is provided within jEDI without the need for the database drivers to provide it themselves: Database journaling. Updates to the database, such as record updates, file clears and so on will be journaled automatically by jEDI assuming the facility is operational. The database driver need not perform any specific operation. There are options in the code for the OPEN to prevent a particular file from being journaled if required. Transaction boundaries. Within jEDI the notion of single level transaction boundaries are supported. This is done by the base code of jEDI. The database driver need not perform any specific operation. There are options in the code for the OPEN to prevent a particular file from being part of a transaction if required. File ajar processing. The routine closing and re-opening of files on a temporary measure is done by the jEDI API, and was described fully in the earlier section File Ajar Processing.

jEDI Database Drivers Manual

7-8

Advanced Programmers Reference

INIT - Initialisation of Database Driver


The first function to write is the main initialisation function that will be called just once, the first time the database driver is referenced. It should set up a structure that defines the database driver, and then call a jEDI function to register the database driver. The name of this function, as explained earlier, can be anything but the name you decide is the name you must specify in the UNIX file that is the file descriptor. For example, if you name this function INIT, typically the UNIX file that describes the shared object database driver would be: % COUNT ./THISFILE 169 RECORDS COUNTED % cat ./THISFILE JBC__OBJ INIT XYZ-INFO^TBAGS Synopsis: int Parameters: None. Return Value: The return value is the database driver number this has been registered as (using function call JediBaseAddDriver()). If an error occurs, return a negative value and set errno set to show the reason for the failure. Operation: The code for this function will look pretty much the same for each database driver. While the user can of course add any extra initialisations they like, the bulk of the code will look like this: int OPEN(), CLOSE(), SELECT(), SELECTEND() ; int READNEXT(), READ(), WRITE(); int DELETE(), LOCK(), IOCTL () ; int CLEAR(), SYNC() ; struct JediDriverStruct p1 ; int DriverNumber ; /* * Initialise the members of the 'p1' structure * with our device driver function addresses. */ memset(&p1,0,sizeof(p1)); p1.FileTypeDescr = "OurDatabase" ; p1.OpenCodePtr = OPEN ; p1.JediClose = CLOSE ; p1.JediSelect = SELECT ; p1.JediSelectEnd = SELECTEND ; p1.JediReadnext = READNEXT ; p1.JediReadRecord = READ ; p1.JediWriteRecord = WRITE ; p1.JediDeleteRecord = DELETE ; p1.JediLock = LOCK ; p1.JediIOCTL = IOCTL ; p1.JediClearFile = CLEAR ; p1.JediSync = SYNC ; /* * Add this to the list of supported * database drivers and return. */ return JediBaseAddDriver(&p1); Basically, all this function does is to set up a structure with details of all its functions to support all the required database I/O requests, and then call JediBaseAddDriver() to register it as a database driver. INIT()

Advanced Programmers Reference Manual

7-9

jEDI Database Drivers

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.

jEDI Database Drivers Manual

7-10

Advanced Programmers Reference

OPEN - Open a File


This function is called whenever the application performs an OPEN request for this particular database driver. Synopsis: static OPEN(FileDescriptor, HeaderInfo, HeaderInfoLen ) JediFileDescriptor * FileDescriptor; char * HeaderInfo ; int HeaderInfoLen ; Parameters: FileDescriptor. (Input and Output parameter). This is the address of a file descriptor that has been partially filled in by the calling jEDI code. The members of the structure filled in before the call to OPEN, and the expected members to be filled in if the OPEN succeeds, are detailed later. HeaderInfo. (Input parameter). This is a pointer to the first n bytes of the UNIX file that was opened. For example, in the case of shared object database drivers, this would typically point to: JBC__SOB INIT AnyOtherCode Second line of description. HeaderInfoLen. (Input parameter). This describes the amount of data pointed to by HeaderInfo. Return Value: 0 shows the file was opened successfully. ENOENT shows the file could not be opened but there is no reason why other database drivers should not attempt to open the file. Any other positive value if a fatal error occurred and no more database drivers should be given the opportunity to open the file. Operation: The OPEN function will now use the information passed in the above three parameters to try to open the relevant file in the foreign database it is accessing. If the OPEN succeeds, the database driver will fill in other parts of FileDescriptor and return a value of 0. How you make the correspondence between the information in FileDescriptor and the foreign database file name is entirely up to the driver. For example, the UNIX file may look like this: JBC__SOB INIT -rcoffice -ausers CUSTOMERS and the database driver may interpret this to mean to open the file CUSTOMERS in remote machine coffice in account users. The example program provided with jBASE, as described earlier, simply concatenates the actual UNIX file name opened with the operands that follow the database function name INIT. Any security and access permissions are entirely the responsibility of the database driver. Just because the user has had access to the file definition doesnt necessarily mean the user is entitled to access the file. It would be too easy for a user simply to edit their own definition of the file and then try to open it. Notes: As mentioned earlier, the information for the user is mostly passed in parameter FileDescriptor. The following is a list of members of FileDescriptor that are initialised by the jEDI code before calling INIT: ProcessId. The process id of the calling process. CurrentDir. The current working directory of the process. PathName. The path name of the file description originally opened. OptionalArgs. The list of text that followed the JBC__SOB FuncName text in the file description originally opened. int

Advanced Programmers Reference Manual

7-11

jEDI Database Drivers

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.

jEDI Database Drivers Manual

7-12

Advanced Programmers Reference

CLOSE - Close an Opened File


This function is called when a file descriptor is closed. Synopsis: static int CLOSE(FileDescriptor, Flags) JediFileDescriptor * FileDescriptor; int Flags ; Parameters: FileDescriptor. (Input and Output parameter). This is the file descriptor that was returned when the file was originally opened. Flags. (Input parameter). This parameter is not used at present. 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 close operation should typically release all resources that was allocated uniquely by the database driver itself. This includes closing down file descriptors, flushing any cache data, releasing workspace uniquely allocated to the file descriptor that was allocated by the database driver etc. The file descriptor may be associated with SELECT structures allocated against the file descriptor. Therefore, any SELECT structures that were allocated against the file descriptor (see SELECT later) must be terminated. Note there can be more than one SELECT associated with a single file descriptor. You should traverse the linked list to clean up these multiple selects. An example is given in the source file jediDExample.c. Once the close functionality has cleaned up its own allocated resources, it must call function JediFreeFileDescriptor as shown below: JediFreeFileDescriptor(FileDescriptor); This will release all the resources allocated by jEDI for the file descriptor, including the structure pointed to by FileDescriptor. Therefore, this call is the last operation you should perform. Notes: The database driver should remove all locks associated with this file with a call to the LOCK function before actually closing a file.

Advanced Programmers Reference Manual

7-13

jEDI Database Drivers

SELECT - Select Record Keys from a File


This function is called when the user wants to perform some sort of selection of the file. At present, the select function is called from a jBC program to select all records in the file. However, calls from C programs and future jBC programs may add selection criteria. Synopsis: static int SELECT(FileDescriptor, SelectPtr, SelectDetails, Index ) JediFileDescriptor * FileDescriptor; struct JediSelectPtr ** SelectPtr; char * SelectDetails; int Index ; Parameters: FileDescriptor. (Input and Output parameter). This is the file descriptor that was returned when the file was originally opened. SelectPtr. (Input and Output parameter). The caller passes here the address of a struct JediSelectPtr * variable. If the SELECT works, then this function will return here the address of an allocated JediSelectPtr structure. This address will be passed to future calls to SELECTEND and READNEXT. SelectDetails. (Input parameter). This is a NULL terminated string describing the selection details. For example, to select all record keys that match the regular expression ^MJI.*PIPE the caller will set this variable to point to the string ^MJI.*PIPE. Note that none of the supplied database drivers as of release 3.0 support this feature. Future database driver may do so. Currently the language for jBC does not support this either, but again may do so in the future. Index. (Input parameter). For database drivers that support multiple indexed file, this shows the index number to perform the select against. As of release 3.0, the jBC language does not directly support multiple indexed databases. However, this may change in future releases. 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 SELECT function will allocate a structure of its own design that describes this selection call. The application will then make repeated calls to the READNEXT function to retrieve the record keys as designated by this SELECT process call. It is up to the designer of the database driver to decide if to make a list of all the record keys during this function call, or just to establish selection criteria and let the READNEXT do the actual selection. A segment of the code to achieve the SELECT would look like this: struct JediSelectPtr *BlockPtr, **LoopPtr; /* * Allocate space for the standard * JediSelectPtr structure. */ if ((BlockPtr=JediMalloc( FileDescriptor, sizeof(struct JediSelectPtr) )) == NULL) { return errno ; } /* * Now to allocate a structure for our own * use. The structure name is * called Select, and it can be anything our * own database driver wants
jEDI Database Drivers Manual 7-14 Advanced Programmers Reference

* 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.

Advanced Programmers Reference Manual

7-15

jEDI Database Drivers

SELECTEND - Terminate a Selection Process


This function is called when the selection process is terminated. For example, in a jBC program this could be in one of the two manners: OPEN FileName TO FileVar ELSE ... SELECT FileVar TO SelectVar LOOP WHILE READNEXT RecordKey FROM SelectVar DO ...... REPEAT or OPEN FileName TO FileVar ELSE ... SELECT FileVar TO SelectVar ...... SelectVar = 0 In the first example the selection process is exhausted. In the second example, the selection process is terminated prematurely, Synopsis: static int SELECTEND(FileDescriptor, SelectPtr ) JediFileDescriptor * FileDescriptor; struct JediSelectPtr * SelectPtr; Parameters: FileDescriptor. (Input and Output parameter). This is the file descriptor that was returned when the file was originally opened. SelectPtr. (Input and Output parameter). This is the address of the structure that was created during the SELECT function. 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 select has ended. This function should clear up all space allocated by the SELECT function call, and remove the SelectPtr structure from the linked list of select structure pointed to in the FileDescriptor structure. In the example code with the call to function SELECT(), there were two structures allocated, and the first was positioned at the end of the linked list. Carrying on from that example, the following code would clean up the resources allocated during the call to SELECT(). struct JediSelectPtr *Next, ** LoopPtr ; /* * Free the structure allocated specifically * for the use of this database driver. * This can be used for whatever purposes * the database driver likes. * It is at this stage the database driver * would clean up all the members * pointed to by SelectPtr->TypePtr. */ JediFree(FileDescriptor,SelectPtr->TypePtr); /* * Extract the pointer to the next structure * in the linked list. */ Next = SelectPtr->NextPtr ; /* * Now to free the other structure allocated in

jEDI Database Drivers Manual

7-16

Advanced Programmers Reference

* 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 ;

Advanced Programmers Reference Manual

7-17

jEDI Database Drivers

READNEXT - Get Next Record Key from Selection


This function is typically called many times following a call to the SELECT function. This function will return the next record that has been selected following a call to the SELECT function. Synopsis: static int READNEXT(FileDescriptor, SelectPtr, RecordKeyPtr, RecordKeyLenPtr) JediFileDescriptor * FileDescriptor; struct JediSelectPtr * SelectPtr; char ** RecordKeyPtr ; int * RecordKeyLenPtr ; Parameters: FileDescriptor. (Input parameter). This is the file descriptor that was returned when the file was originally opened. SelectPtr. (Input parameter). This is the address of the structure that was created during the SELECT function. RecordKeyPtr. (Input and Output parameter). The application passes in the parameter a pointer to a pointer to a buffer. This is where the next selected record key will be returned. If the buffer is not large enough (See RecordKeyLenPtr), then the READNEXT function should allocate sufficient memory and return at RecordKeyPtr the address of the allocated memory. It is up to the calling application to detect if this has been performed, and to free any allocated memory. RecordKeyLenPtr. (Input and Output parameter). This is the address of an integer that describes the length of the buffer given by the expression *RecordKeyPtr. When we have selected the next record key and placed it in the buffer *RecordKeyPtr, we return here the length of the record key. If there are no more record keys, then -1 is returned here. Return Value: 0 if the operation succeeded. This includes when the list of record keys are exhausted (in this case, *RecordKeyLenPtr set to -1). Any other value shows the reason the operation failed using the values defined in errno.h. Operation: This function simply reads the next record key from the select list and returns details of the record key. At the end of processing all keys, we return -1 in the RecordKeyLenPtr variable. This process is best illustrated by example. The example below will simply return 3 record keys called RecordKey1, RecordKey2 and RecordKey3. It is assumed that the previous call to SELECT will have resulted in the members maxkey being set to 3 and currkey set to 0. struct int int } ; Sel { maxkey ; currkey ;

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 ;

Advanced Programmers Reference Manual

7-19

jEDI Database Drivers

READ - Read a Record from a File


This function is called when the application wants to read a record from the opened file. Synopsis: static int READ(FileDescriptor, Flags,RecordKey, RecordKeyLen, BufferPtr, BufferLenPtr, FieldNumber) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; char ** BufferPtr ; int * BufferLenPtr ; int FieldNumber ; Parameters: FileDescriptor. (Input parameter). This is the file descriptor that was returned when the file was originally opened. Flags. (Input parameter). One or more of the following bits. JEDI_RECORD_FIELDREADWRITE. If set, then the value at parameter FieldNumber contains the field number (or attribute) to read, instead of the entire record. If the database drive doesnt support the reading of individual fields, then it should have not set the JEDI_STATUS_FIELDREAD bit during the OPEN function (see the OPEN function for details of the Status member of JediFileDescriptor) and this can be ignored.

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).

jEDI Database Drivers Manual

7-20

Advanced Programmers Reference

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 ;

Advanced Programmers Reference Manual

7-21

jEDI Database Drivers

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.

jEDI Database Drivers Manual

7-22

Advanced Programmers Reference

WRITE - Write a Record to a File


This function is called when the application wants to write a record to the file. Synopsis: static int WRITE(FileDescriptor, Flags, RecordKey, RecordKeyLen, BufferPtr, BufferLen, FieldNumber) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; char * BufferPtr ; int BufferLen ; int FieldNumber ; Parameters: FileDescriptor. (Input parameter). This is the file descriptor that was returned when the file was originally opened. Flags. (Input parameter). One or more of the following bits. JEDI_RECORD_FIELDREADWRITE. If set, then the value at parameter FieldNumber contains the field number (or attribute) to write, instead of the entire record. If the database drive doesnt support the writing of individual fields, then it should have not set the JEDI_STATUS_FIELDWRITE bit during the OPEN function (see the OPEN function for details of the Status member of JediFileDescriptor) and this can be ignored.

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.

Advanced Programmers Reference Manual

7-23

jEDI Database Drivers

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.

jEDI Database Drivers Manual

7-24

Advanced Programmers Reference

DELETE - Delete a Record from a File


This function is called when the application wants to delete a record from the file. Synopsis: static int DELETE(FileDescriptor, Flags, RecordKey, RecordKeyLen ) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; Parameters: FileDescriptor. (Input parameter). This is the file descriptor that was returned when the file was originally opened. Flags. (Input parameter). This parameter not used at present. RecordKey. (Input parameter). Points to a string describing the record key to delete. Note the record key is not a 0 terminated string. RecordKeyLen. (Input parameter). This is the length of the record key as passed by RecordKey. Return Value: 0 is returned if the record is deleted successfully. ENOENT is returned if the record did not exist. Any other value shows the reason the operation failed using the values defined in errno.h. Operation: The DELETE function simply removes the record from the database. The following is a very simple example of deleting a record, where we have assumed the database is simply a UNIX flat file. char ActualPathName[PATH_MAX+1]; int i1, fd1 ; /* * Make up the name of a UNIX file to delete. */ i1 = strlen(FileDescriptor->PathName); memcpy(ActualPathName,FileDescriptor->PathName,i1); ActualPathName[i1] = '/'; memcpy(&ActualPathName[i1+1],RecordKey, RecordKeyLen); ActualPathName[i1+1+RecordKeyLen] = NULL; /* * Now to delete the actual record. */ if (unlink(ActualPathName) != 0) { return (errno == 0 ? ENOENT : errno); } return 0 ; There is no locking flags passed to the DELETE function. The is because the application request to delete a record 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 release any record locking. Hence, there is no requirement for the DELETE functionality in the database driver to explicitly worry about locks.

Advanced Programmers Reference Manual

7-25

jEDI Database Drivers

CLEAR - Delete All Records from a File


This function is called by an application to delete all the records from a file. No file locks are taken, so other applications could be writing to the file at the same time. Synopsis: static int CLEAR(FileDescriptor) JediFileDescriptor *FileDescriptor; Parameters: FileDescriptor. (Input 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 database driver simply clears all records from the file in the most efficient manner it can. It is up to the database driver to decide if to return an error code if some of the records cannot be deleted. For example, in the driver supplied with jBASE to use UNIX files as a database, if some of the records cannot be deleted (i.e. the UNIX file cannot be unlinked), for example because of file permissions, then no error is reported. However, the database driver is free to choose whether to ignore such failures or not. The following crude example shows how a database drive that uses the UNIX files as a database might delete all records (i.e. UNIX files). DIR struct char struct *DirPtr; dirent *Next; PathName[PATH_MAX*2]; stat stat_info ;

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;

jEDI Database Drivers Manual

7-26

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

7-27

jEDI Database Drivers

LOCK - Provide Record Locking Mechanism


This function is called by the application to provide record locking support. It can also be called intrinsically via the jEDI code to support record locking functions. For example, if an application performs a request to READ a record with a lock, then the jEDI function will call the LOCK function directly, rather than the READ function doing the call itself. Synopsis: static int LOCK(FileDescriptor, Flags, RecordKey, RecordKeyLen ) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; Parameters: FileDescriptor. (Input parameter). This is the file descriptor that was returned when the file was originally opened. Flags. (Input parameter). Can be one of the following bits to show what operation is to be performed by the LOCK function: JEDI_RECORD_LOCKRECORD Take a record lock and wait. JEDI_RECORD_LOCKRECORD_NOWAIT Take a record lock, but return if lock already taken (The function return code is JEDI_ERRNO_LOCK_TAKEN). JEDI_RECORD_UNLOCKRECORD Remove a record lock. RecordKey. (Input parameter). This is a pointer to a character array which shows what record key is to be locked or unlocked. Note that this array is not a 0 terminated string (See RecordKeyLen parameter). A special case exists for when NULL is passed in this field. This means the calling application wants to release all the locks for all the records in the file held by this application. RecordKeyLen. (Input parameter). This is the length of data passed in the RecordKey parameter. Return Value: 0 shows the locks were created/removed successfully. JEDI_ERRNO_LOCK_TAKEN the JEDI_RECORD_LOCKRECORD_NOWAIT bit was set, and the record/field was already locked by another process. EDEADLK shows one of the lock bits was set, and a deadly embrace situation was detected and avoided. Any other value shows the reason the operation failed using the values defined in errno.h. Operation: The function should check if RecordKey is NULL. If it is, then release all locks held by this application for this file. If the RecordKey is not NULL, then the function should either take or release an individual record key lock. A locking function is supplied with jBASE called JediSystemLock that can be used to provide a record locking scheme. It is not a 100% genuine record locking scheme, but a scheme whereby a pseudo-random integer value is created from the record key and the pseudo-random value is the value that is locked. While not a 100% record locking scheme, the chances of a clash whereby different record keys generate the same pseudo-random value are very small. The following shows how the LOCK function could be written using the supplied JediSystemLock() function. However, the database driver is free to use alternate locking mechanisms if required. In this case the LOCK function will probably become an interface to the record locking scheme provided by the foreign database. unsigned int HashValue ;

jEDI Database Drivers Manual

7-28

Advanced Programmers Reference

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 ) ;

Advanced Programmers Reference Manual

7-29

jEDI Database Drivers

IOCTL - Support Database Driver Control Functions


This function is called to provide general database driver operations. Like the ioctl() function in C programs, there are some standard commands that most database drivers will be expected to perform, some that are optional, and some that are unique to the database driver. Synopsis: static int IOCTL (FileDescriptor, SubCommand, IoctlAddr, IoctlLen, IoctlReturnAddr, IoctlReturnLen) JediFileDescriptor * FileDescriptor; int SubCommand ; void * IoctlAddr ; int IoctlLen ; void * IoctlReturnAddr ; int * IoctlReturnLen; Parameters: FileDescriptor. (Input parameter). This is the file descriptor that was returned when the file was originally opened. SubCommand. (Input parameter). This shows actually what sort of operation to perform. There are some common commands given by the JEDI_IOCTL_xxx definition, but the database driver can add support for other commands if required. IoctlAddr. (Input parameter). This gives the address of any optional buffer that may be necessary to provide extra specification depending on the SubCommand. Therefore its use is highly dependent upon SubCommand. The application may pass NULL here. IoctlLen. (Input parameter). This is the size of data that is pointed to by the IoctlAddr parameter. IoctlReturnAddr. (Input and Output parameter). This is the address of a buffer or length IoctlReturnLen bytes where the IOCTLfunction can return any data depending upon the command to be executed (See SubCommand). IoctlReturnLen. (Input and Output parameter). This is the address of an integer that shows the length of the return buffer pointed to by IoctlReturnAddr. Upon exit, the database driver should update this to show the amount of valid data that the IOCTL function has returned in IoctlReturnAddr. Return Value: The return value is highly dependent on the database driver. By convention though you should return -1 if the operation failed, 0 if the operation was successful. In the example code given, if you decide to include the standard IOCTL functions, then return the same return code as given in the example code. Operation: The use of IOCTL is highly database driver dependent. However, there are some common functions that a database driver may be expected to perform. The code example below shows all these common functions. All the functionality is optional, and all the jBC utilities that make IOCTL calls have suitable defaults should the database driver decide not to support the command. int int char char char struct char int int ReturnValue; i1, ReturnBufferLen ; ReturnBuffer[4096]; FullName[4096]; *Ptr1, *Ptr2 ; stat RecStat ; ActualPathName[PATH_MAX+1]; SaveErrno ; fd1 ;

ReturnValue = 0; switch(SubCommand)

jEDI Database Drivers Manual

7-30

Advanced Programmers Reference

{ 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 ;

Advanced Programmers Reference Manual

7-31

jEDI Database Drivers

} 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 ;

jEDI Database Drivers Manual

7-32

Advanced Programmers Reference

SYNC - Synchronise the Data to Disk


This function is called to flush any cache data to the disk. This is quite specialised and if necessary the database driver can choose to do nothing except give a return code of 0. Synopsis: static int SYNC(FileDescriptor) JediFileDescriptor *FileDescriptor; Parameters: FileDescriptor. (Input 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 following code example shows the use of the UNIX fsync() function to provide this functionality. Note that not all derivatives of UNIX support fsync(). errno = 0; if (fsync(FileDescriptor->FileFd) == 0) return(0); return errno;

Advanced Programmers Reference Manual

7-33

jEDI Database Drivers

Chapter 8: jEDI API Calls

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.

Advanced Programmers Reference Manual

8-1

jEDI API Calls

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. */

jEDI API Calls Manual

8-2

Advanced Programmers Reference

Making jEDI Database Requests


Once any initialisation has been performed, you can start calling jEDI API functions. The first call is usually to JediOpen, followed by a number of requests using the returned file descriptor. One of the limitations during this phase is that the C program must keep the environment variable PWD up to date. The variable PWD is initialised to the current directory by jEDI when the first ever JediOpen function call is made. However, should the program change the directory using calls to chdir, they must also update the PWD directory. Here is a summary of the function calls, a full description is given later,: JediOpen Open a file.

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.

Advanced Programmers Reference Manual

8-3

jEDI API Calls

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.

jEDI API Calls Manual

8-4

Advanced Programmers Reference

Compiling and Linking a Program


Once you have written your C application, you need to compile it and link it. There are two considerations here. First, where to find the #include files jsystem.h and jedi.h and second, where to find the jedi library.

Finding the #include Files.


If you use the cc command to build your application, you need to specify on the command line where the include files are, for example: % cc -c myprog.c -I $JBCRELEASEDIR/include If you use the jbc command to build your application, the command is simplified: % jbc -c myprog.c

Linking with the jEDI Library.


If you use the cc command to build your application, you need to specify where the the jEDI API functions library can be found. There are a number of ways to achieve this. The following examples may vary slightly with different UNIX development environments. The first example shows linking with shared objects using the cc command: % cc myprog.o -o myprog -L $JBCRELEASEDIR/lib -l jedi The next example shows linking with archive libraries:
% cc myprog.o -o myprog -s $JBCRELEASEDIR/lib/libjedi.a

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

Advanced Programmers Reference Manual

8-5

jEDI API Calls

Transaction Boundary Support


This can be provided within a C program using the calls to the supplied function JediTransLog. There are basically four calls - start, end (commit), abort (rollback) and query. Transactions cannot be nested, so if you start a transaction when a transaction is already active, an error code is returned.

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.

Ending (Committing) a Transaction


if (JediTransLog(JEDI_TRANSLOG_COMMAND_END, Flags, message) != 0) { perror(TRANSEND); } Where: JEDI_TRANSLOG_COMMAND_END Flags Tells the function to end or commit a transaction.

Not used with this function call.

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.

Aborting (Rollback) a Transaction:


if (JediTransLog(JEDI_TRANSLOG_COMMAND_ABORT, Flags, message) != 0) { perror(TRANSABORT); } Where: JEDI_TRANSLOG_COMMAND_ABORT Flags Not used with this function call. Tells the function to abort (or rollback) a transaction.

jEDI API Calls Manual

8-6

Advanced Programmers Reference

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.

Query the Status of a Transaction:


if (JediTransLog(JEDI_TRANSLOG_COMMAND_QUERY)) { printf(A transaction is active\n); } else { printf(No transaction open\n); } Where: JEDI_TRANSLOG_COMMAND_QUERY Tells the function that this is a query. The return value is 0 if the process is not in a transaction, or non-zero if the process is inside a transaction.

Advanced Programmers Reference Manual

8-7

jEDI API Calls

jEDI Environment Variables


When using a program with jEDI, you should be aware of some of the environment variables used by the jEDI library: PWD. This will be initialised by jEDI following the first call to JediOpen. Once set up by jEDI, the program must keep this up to date should the program call chdir() at any point to change the current working directory. HOME. This defines the home directory of the logged on user. It is used by jEDI when resolving Q pointers which point to an alternative user name. LD_LIBRARY_PATH. Used by most UNIX systems to define the path to look for shared objects. If you write a C program that was linked with jEDI shared objects, before the program can be executed you must set LD_LIBRARY_PATH to include, as one of its components, the name of the directory where it can find the jEDI shared object. LIBPATH. Same as LD_LIBRARY_PATH, but for AIX systems. JEDIFILENAME_MD. This defines whether you have a Master Dictionary file in operation. If so, during a call to JediOpen the function will look in this file to see if the file is defined here. If a record exists in the file defined by JEDIFILENAME_MD, and the first field is Q, this is a Q pointer and is resolved as such. JEDIFILENAME_SYSTEM. If this variable is set, it is used by jEDI to find out more about pseudo jBASE account names during Q pointer resolution. JEDIFILEPATH. When a file is opened, it is usually by a relative name such as CUSTOMERS or PIPES. This variable can contain a number of directory names, delimited by a colon, to describe the search path to look for the file. For example, if the environment variable JEDIFILEPATH contained /home/greg:/home/fat, jEDI would first try to open file /home/greg/CUSTOMERS followed by /home/fat/CUSTOMERS. If this variable is not defined, jEDI defaults to the home directory followed by the current directory. JBCOBJECTLIST. This is used when attempting to load user-written database drivers. It gives a list of directories where the shared objects may exist. See also the description of the JediReinitialise() function later on in this chapter. JediReinitialise may need to be called if you change these environment variables within your program.

jEDI API Calls Manual

8-8

Advanced Programmers Reference

JediOpen - Open a File


This function is called whenever the application needs to open a file. Synopsis: int JediOpen(ConnectHandle, FilePointer, PathName, FilePath ); int ConnectHandle ; JediFileDescriptor ** FilePointer ; char * PathName ; char * FilePath ; Parameters: ConnectHandle. This is the value passed back by the call to JediConnect, that was documented earlier in this chapter. If the call to JediConnect was omitted, simply pass 0 in this parameter. FilePointer. The address of a file descriptor pointer. If the open is successful, we return here the file descriptor. This file descriptor is passed on all subsequent jEDI API calls to define the opened file. PathName. This is the name of the file to open, and can be in a number of formats. Examples are: CUSTOMERS. The file name to open is CUSTOMERS, and the open function will make use of the FilePath parameter to establish the directory that CUSTOMERS is contained in. ./CUSTOMERS. The file name to open is CUSTOMERS, but the use of the preceding ./ means we ignore the FilePath parameter and just look in the current directory. ../CUSTOMERS]D. Open the dictionary section of file CUSTOMERS, which will have a UNIX file name of CUSTOMERS]D. The use of the preceding ../ means we ignore the FilePath parameter and just look in the parent directory. DICT PROSPECTS. Open the dictionary section of file PROSPECTS. The open function will make use of the FilePath parameter to establish the directory that CUSTOMERS is contained in. /home2/accts/ORDERS,1995. Open the section 1995 of the file ORDERS, which is in directory /home2/accts. The FilePath parameter will not be used. FilePath. If a simple file name is given to be opened, without a leading / or ./ or ../,we use this parameter to establish what directories to look in. Each directory name in this parameter is delimited by a colon. You would normally pass NULL here, and JediOpen will use the environment variable JEDIFILEPATH instead. Note that if FilePath is NULL, and JEDIFILEPATH is undefined, we search for the file in the users home directory followed by the current working directory. Return Value: 0 shows the file was opened successfully. ENOENT shows the file could not be opened. Any other value shows the reason the operation failed using the values defined in errno.h.

Advanced Programmers Reference Manual

8-9

jEDI API Calls

JediOpenDeferred - Deferred Open


This function is called whenever the application requires a file to be opened. It is similar to the JediOpen function call, except that the file is only pseudo-opened. In fact, the function will almost always succeed. The first database operation performed on the file descriptor will cause the file to be actually opened. If this open fails, an appropriate error return code is returned to the caller of the database operation. The use of this function is highly specialised. One use of it is where, as part of a global common initialisation routine, you open all the files you may ever use. This is a common technique in application programming. By calling JediOpenDeferred you have a much lower start-up overhead, and only incur the overhead of a real open when that file is actually used. Synopsis: int JediOpenDeferred(ConnectHandle, FilePointer, PathName, FilePath ); int ConnectHandle ; JediFileDescriptor ** FilePointer ; char * PathName ; char * FilePath ; Parameters: ConnectHandle. This is the value passed back by the call to JediConnect, that was documented earlier in this chapter. If the call to JediConnect was omitted, simply pass 0 in this parameter. FilePointer. The address of a file descriptor pointer. If the open is successful, we return the file descriptor here. This file descriptor is passed on all subsequent jEDI API calls to define the opened file. PathName. This is the name of the file to open, and can be in a number of formats. Examples are: CUSTOMERS. The file name to open is CUSTOMERS, and the open function will make use of the FilePath parameter to establish the directory that CUSTOMERS is contained in. ./CUSTOMERS. The file name to open is CUSTOMERS, but the use of the preceding ./ means we ignore the FilePath parameter and just look in the current directory. ../CUSTOMERS]D. Open the dictionary section of file CUSTOMERS, which will have a UNIX file name of CUSTOMERS]D. The use of the preceding ../ means we ignore the FilePath parameter and just look in the parent directory. DICT PROSPECTS. Open the dictionary section of file PROSPECTS. The open function will make use of the FilePath parameter to establish the directory that CUSTOMERS is contained in. /home2/accts/ORDERS,1995. Open the section 1995 of the file ORDERS, which is in directory /home2/accts. The FilePath parameter will not be used. FilePath. If a simple file name is given to be opened, without a leading / or ./ or ../, we use this parameter to establish what directories to look in. Each directory name in this parameter is delimited by a colon. You would normally pass NULL here, and JediOpen will use the environment variable JEDIFILEPATH instead. Note that if FilePath is NULL, and JEDIFILEPATH is undefined, we search for the file in the users home directory followed by the current working directory. Return Value: 0 shows the file was opened successfully. ENOENT shows the file could not be opened. Any other value shows the reason the operation failed using the values defined in errno.h.

jEDI API Calls Manual

8-10

Advanced Programmers Reference

JediClose - Close an Opened File


This function is called to close an opened file. Any locks that exist will be released. If the file is currently part of a transaction, the jEDI code will defer the close until the end of the transaction - no effort is required on the part of the application and it should assume the file is really closed. Synopsis: int JediClose(FileDescriptor, Flags) JediFileDescriptor * FileDescriptor; int Flags ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Flags. This parameter is not used at present. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h.

Advanced Programmers Reference Manual

8-11

jEDI API Calls

JediSelect - Select Record Keys from a File


This function is called when the user wants to perform some sort of selection of the file. At present, the select function is called to select all records in the file. However, calls from future programs may add selection criteria. Synopsis: int JediSelect(FileDescriptor, SelectPtr, SelectDetails, Index ) JediFileDescriptor * FileDescriptor; struct JediSelectPtr ** SelectPtr; char * SelectDetails; int Index ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. SelectPtr. The caller passes the address of a struct JediSelectPtr * variable here. If the SELECT works, this function will return here the address of an allocated JediSelectPtr structure. This address will be passed to future calls to SELECTEND and READNEXT. SelectDetails. This is a NULL terminated string describing the selection details. For example, to select all record keys that match the regular expression ^MJI.*PIPE, the caller will set this variable to point to the string ^MJI.*PIPE. Note that none of the supplied database drivers as of release 3.0 support this feature. Future database driver may do so. Currently the language for jBC does not support this either, but again may do so in the future. Index. For database drivers that support multiple indexed file, this shows the index number to perform the select against. As of release 3.0, the jBC language does not directly support multiple indexed databases. However, this may change in future releases. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h.

jEDI API Calls Manual

8-12

Advanced Programmers Reference

JediSelectEnd - Terminate a Selection Process


This function is called when the selection process is terminated. Synopsis: int JediSelectEnd(FileDescriptor, SelectPtr ) JediFileDescriptor * FileDescriptor; struct JediSelectPtr * SelectPtr; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. SelectPtr. This is the address of the structure that was created during the call to the JediSelect function. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h.

Advanced Programmers Reference Manual

8-13

jEDI API Calls

JediReadnext - Get Next Record Key


This function is typically called many times following a call to the SELECT function. This function will return the next record that has been selected following a call to the SELECT function. Synopsis: int JediReadnext(FileDescriptor, SelectPtr, RecordKeyPtr, RecordKeyLenPtr) JediFileDescriptor * FileDescriptor; struct JediSelectPtr * SelectPtr; char ** RecordKeyPtr ; int * RecordKeyLenPtr ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. SelectPtr. This is the address of the structure that was created during the SELECT function. RecordKeyPtr. The application passes in the parameter a pointer to a pointer to a buffer. This is where the next selected record key will be returned. If the buffer is not large enough (See RecordKeyLenPtr), the JediReadnext function will allocate sufficient memory and return at RecordKeyPtr the address of the allocated memory. It is up to the calling application to detect if this has been performed, and to free any allocated memory. RecordKeyLenPtr. This is the address of an integer that describes the length of the buffer given by the expression *RecordKeyPtr. When we have selected the next record key and placed it in the buffer *RecordKeyPtr, we return here the length of the record key. If there are no more record keys, -1 is returned here. Return Value: 0 if the operation succeeded. This includes when the list of record keys are exhausted (in this case, *RecordKeyLenPtr set to -1). Any other value shows the reason the operation failed using the values defined in errno.h.

jEDI API Calls Manual

8-14

Advanced Programmers Reference

JediReadRecord - Read a Record from a File


This function is called when the application wants to read a record from the opened file. Synopsis: int JediReadRecord(FileDescriptor, Flags,RecordKey, RecordKeyLen, BufferPtr, BufferLenPtr, FieldNumber) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; char ** BufferPtr ; int * BufferLenPtr ; int FieldNumber ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Flags. One or more of the following bits. JEDI_RECORD_FIELDREADWRITE. If set, the value at parameter FieldNumber contains the field number (or attribute) to read, instead of the entire record. If the database drive doesnt support the reading of individual fields, it will not set the JEDI_STATUS_FIELDREAD bit during the JediOpen function and this will be ignored.

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.

Advanced Programmers Reference Manual

8-15

jEDI API Calls

JediWriteRecord - Write a Record to a File


This function is called when the application wants to write a record to the file. Synopsis: int JediWriteRecord(FileDescriptor, Flags, RecordKey,RecordKeyLen, BufferPtr, BufferLen, FieldNumber) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; char * BufferPtr ; int BufferLen ; int FieldNumber ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Flags. One or more of the following bits. JEDI_RECORD_FIELDREADWRITE. If set, the value at parameter FieldNumber contains the field number (or attribute) to write, instead of the entire record. If the database driver doesnt support the writing of individual fields, it will not set the JEDI_STATUS_FIELDWRITE bit during the JediOpen function and this will be ignored.

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.

jEDI API Calls Manual

8-16

Advanced Programmers Reference

JediDelete - Delete a Record from a File


This function is called when the application wants to delete a record from the file. Synopsis: int JediDelete(FileDescriptor, Flags, RecordKey, RecordKeyLen ) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Flags. This parameter not used at present. RecordKey. Points to a string describing the record key to delete. Note the record key is not a 0 (null) terminated string. RecordKeyLen. This is the length of the record key as passed by RecordKey. Return Value: 0 is returned if the record is deleted successfully. ENOENT is returned if the record did not exist. Any other value shows the reason the operation failed using the values defined in errno.h.

Advanced Programmers Reference Manual

8-17

jEDI API Calls

JediLock - Provide Record Locking Mechanism


This function is called by the application to provide record locking support. It can also be called intrinsically via the jEDI code to support record locking functions. For example, if an application performs a request to READ a record with a lock, the jEDI function will call the JediLock function directly, rather than the JediReadRecord function doing the call itself. Synopsis: int JediLock(FileDescriptor, Flags, RecordKey, RecordKeyLen ) JediFileDescriptor * FileDescriptor; int Flags ; char * RecordKey ; int RecordKeyLen ; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Flags. Can be one of the following bits to show what operation is to be performed by the LOCK function: JEDI_RECORD_LOCKRECORD Take a record lock and wait. JEDI_RECORD_LOCKRECORD_NOWAIT Take a record lock, but return if lock already taken (The function return code is JEDI_ERRNO_LOCK_TAKEN). JEDI_RECORD_UNLOCKRECORD Remove a record lock. RecordKey. This is a pointer to a character array which shows what record key is to be locked or unlocked. Note that this array is not a 0 terminated string (See RecordKeyLen parameter). A special case exists for when NULL is passed in this field. This means the calling application wants to release all the locks for all the records in the file held by this application. RecordKeyLen. This is the length of data passed in the RecordKey parameter. Return Value: 0 shows the locks were created/removed okay. JEDI_ERRNO_LOCK_TAKEN the JEDI_RECORD_LOCKRECORD_NOWAIT bit was set, and the record/field was already locked by another process. EDEADLK shows one of the lock bits was set, and a deadly embrace situation was detected and avoided. Any other value shows the reason the operation failed using the values defined in errno.h.

jEDI API Calls Manual

8-18

Advanced Programmers Reference

JediIOCTL - Database Driver Control Functions


This function is called to provide general database driver operations. Like the ioctl() function in C programs, there are some standard commands that most database drivers will be expected to perform, some that are optional, and some that are unique to the database driver. Synopsis: int JediIOCTL(FileDescriptor, SubCommand, IoctlAddr, IoctlLen, IoctlReturnAddr, IoctlReturnLen) JediFileDescriptor * FileDescriptor; int SubCommand ; void * IoctlAddr ; int IoctlLen ; void * IoctlReturnAddr ; int * IoctlReturnLen; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. SubCommand. This shows actually what sort of operation to perform. There are some common commands given by the JEDI_IOCTL_xxx definition, but the database driver can add support for other commands if required. IoctlAddr. This gives the address of any optional buffer that may be necessary to provide extra specification, depending on the SubCommand. Therefore its use is highly dependent upon SubCommand. The application may pass NULL here. IoctlLen. This is the size of data that is pointed to by the IoctlAddr parameter. IoctlReturnAddr. This is the address of a buffer or length IoctlReturnLen bytes where the IOCTL function can return any data depending upon the command to be executed (See SubCommand). IoctlReturnLen. This is the address of an integer that shows the length of the return buffer pointed to by IoctlReturnAddr. Upon exit, the database driver should update this to show the amount of valid data that the JediIOCTL function has returned in IoctlReturnAddr. Return Value: The return value is highly dependent on the database driver. By convention, -1 is returned if the operation failed, and 0 if the operation was successful.

Advanced Programmers Reference Manual

8-19

jEDI API Calls

JediSync - Synchronise the Data to Disk


This function is called to flush any cache data to the disk. Not all database drivers support this - those that dont simply return 0. Synopsis: int JediSync(FileDescriptor) JediFileDescriptor *FileDescriptor; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h.

jEDI API Calls Manual

8-20

Advanced Programmers Reference

JediClearFile - Delete All Records from a File


This function is called by an application to delete all the records from a file. No file locks are taken, so other applications could be writing to the file at the same time. Synopsis: int JediClearFile(FileDescriptor) JediFileDescriptor *FileDescriptor; Parameters: FileDescriptor. This is the file descriptor that was returned when the file was originally opened. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h.

Advanced Programmers Reference Manual

8-21

jEDI API Calls

JediPerror - Print Error Message


This function is similar to the standard C library function perror(). However, some of the values for 'errno' that can be returned by certain jEDI function calls are an extension to the standard values for errno. The replacement function for perror() allows for the extensions. Synopsis: int int char Parameters: ErrorValue. This is the error number for which you want the appropriate error message displayed. Under normal circumstances you would pass the global 'errno' value in this parameter. Description. The text associated with the error, in the same was you pass text to the perror() function. Return Value: Always the same as the 'ErrorValue' parameter. Example: if ((returncode = JediReadRecord(FileDescriptor ..... { return JediPerror (returncode , "filename") ; } JediPerror (ErrorValue , Description ) ErrorValue ; * Description ;

jEDI API Calls Manual

8-22

Advanced Programmers Reference

JediFileOp - General File Maintenance


This function allows the caller to create, clear and delete files in the database types that are supported intrinsically by jEDI. The calls to this function will not allow these file operations on external databases, and operations such as delete file should be performed by calling the services provided by the external database. You can also call an external program to perform the functionality rather than call this function directly. For example, instead of calling JediFileOp to create a file, perform the following code : if (system("CREATE-FILE FILENAME 1,1 23,1") != 0) { fprintf(stderr,"CREATE-FILE failed\n"); } Synopsis: int JediFileOp (command, flags, filespec, unixoptions, legacyoptions, errormessage, interrupt) int command ; int flags ; char * filespec ; char * unixoptions ; char * legacyoptions ; char * errormessage ; void (* interrupt)() ; Parameters: command. One of the following values : JEDI_FILEOP_CREATE_FILE JEDI_FILEOP_DELETE_FILE JEDI_FILEOP_CLEAR_FILE flags. This is one of the following values : JEDI_FILEOP_DICT_ONLY JEDI_FILEOP_DATA_ONLY Create only the DICTionary Create only the data section. Create a file Delete a file Clear a file

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.

Advanced Programmers Reference Manual

8-23

jEDI API Calls

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) ; }

jEDI API Calls Manual

8-24

Advanced Programmers Reference

JediReinitialise - Re-initialise jEDI Code


This function is called by an application to re-initialise certain aspects of jEDI when the underlying control environment variables have been changed since the first call to jEDI. The environment variables are: . HOME - The home directory of the user. . JEDIFILEPATH - The list of directories to look in when opening a file. . JEDIFILENAME_MD - The name of the MD file, if any, to use to resolve Q pointers, F pointers and so on. . JEDIFILENAME_SYSTEM - The name of the SYSTEM file, if any, to use to find any pseudo account names during the resolution of Q pointers. Unless you change any of the above environment variables within a program, you will not need to call JediReinitialise. Synopsis: int Parameters: None. Return Value: 0 if the operation succeeded. Any other value shows the reason the operation failed using the values defined in errno.h. Example: /* * About to change the JEDIFILEPATH environment * variable. */ putenv("JEDIFILEPATH=.:/home2/pipeman/files"); /* * If we do not call JediReinitialise, the changed * environment variable will have no effect. */ if ((returncode = JediReinitialise ()) != 0) { JediPerror (returncode , "JediReinitialise"); } JediReinitialise ()

Advanced Programmers Reference Manual

8-25

jEDI API Calls

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

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

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

Advanced Programmers Reference Manual

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

Advanced Programmers Reference

Chapter 10: jbc and BASIC

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.

Advanced Programmers Reference Manual

10-1

jbc and BASIC

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.

jBC Compiler Order of Processing

How to Use the jbc Command


The jbc command is primarily used for the compilation of source programs written in the jBC application programming language. Although designed for jBC, the jbc command also compiles source programs written in C, enabling the programmer to mix and manage both languages in one compilation process. The jbc command normally produces an executable UNIX file with all stages in between transparent to the user, in the same way the UNIX command cc handles C code. jbc effectively replaces the cc command for jBC application programmers.

jbc and BASIC Manual

10-2

Advanced Programmers Reference

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.

jbc cc Only Options


There are a number of options supported by the jbc command that are passed directly to the cc command during the C source compilation and linking phases. The exact meanings of these options depend upon the C development system and the command man cc or man ld will usually describe them. These options are: -a -x -W -d -z -X -e -D -Z -f -E -g -H -p -M -q -N -r -O -s -P -t -U -u -V

jbc Common cc and jbc Options.


These options are used by both the cc command and the jbc command. Option -c Description Suppress the generation of the executable. This will normally result in just the object code (the .o file) being created. If the -Ja option has been used, the -c flag is assumed. Specifies additional library names. Overrides the name of the output executable file. The default is to create an executable called a.out. Specifies the name of a directory (or a jBC file) where sources can be included from. This is used when compiling the INCLUDE or $INCLUDE statement in a jBC source, and the #include statement in a C source. See Include File and Library Processing later. Specifies alternate directories in which to find the libraries. Synonymous with the -S option in the C compiler. Causes the compilation of jBC source code to be terminated after the

-llibname -ofilename -Idirname

-Ldirname -S

Advanced Programmers Reference Manual

10-3

jbc and BASIC

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.

jbc Only Standard Options


These are normal options used purely by the jbc program. Option -h -Jalibname -Jd Description Display the help text. Add input sources or objects to the archive library libname. The link phase (where an executable is created) is omitted. Delete any intermediate object (.o) files produced in the compilation process of a jBC source. The object files produced while compiling native C code remain. Do not run the mcs command after an executable program has been created. This may be useful when using other UNIX tools against an executable program. This allows an easy way of defining the output executable name based on the first jBC source name. For example, if the first jBC source name was prog1.b and the -Jo option was used, the output executable name will be prog1, instead of the default of a.out. Display statistics about compilation times. Verbose mode. The jbc program will display all the sub-programs it calls to complete the command. This is very useful to get a thorough grasp of exactly what jbc is doing. Check that COMMON variables are assigned in the program. Suppress warnings of unresolved labels. {num} specifies how many source code lines to count before printing the lines compiled indicator when -JCv is specified. Produce compilation statistics about the source file. Suppress warnings of unassigned variables. Operate in verbose mode. Indicate the current source file and print the lines compiled indicator. Specify the warning level from 0 to 3. See the later section entitled jbc and BASIC warning levels. Ensure that ANSI code is produced (this may be the default if your system has an ANSI compiler). Define symbol for use in #ifdef directives.

-Jm

-Jo

-Js -Jv

-JCi -JCl -JCm{num} -JCt -JCu -JCv -JCw[0-3] -JCA -JCDsymbol

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

jbc and BASIC Manual

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

jbc SQL Options


These options are used only when compiling sources with embedded SQL statements, which is an optional feature of jBASE. Option -JkKey -Jqi -Jqo -Jqx -Jqm Description Specify SQL key Shows the embedded SQL statements are in Ingres format. Shows the embedded SQL statements are in Oracle format. Shows the embedded SQL statements are in Informix format. Shows the embedded SQL statement are in Microsoft format. This is the default for embedded SQL statements.

jbc C++ Options


These options are used to create C++ code instead of C code from a jBC source. Option -JGg -JGt{in} -JGt Description Operate as normal but generate C++ code and use the C++ compiler to generate object code; Specifies that the compiler should translate to C++ then stop. The jbc command will only operate on one file at a time. Translate the file and follow include files, generating code for any include files as well. In other words, expand INCLUDE statements so that there is only 1 final C++ .C file. The file to translate is an INCLUDEd file and therefore do not generate a .j data file or generate code for the main() or function() section of a C++ source code. Files translated in this way are

-JGti

Advanced Programmers Reference Manual

10-5

jbc and BASIC

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

INCLUDE File and Library Processing


When the jbc command is used to compile jBC and C sources, the sources themselves can declare other sources to be included with the INCLUDE and $INCLUDE statements in a jBC source, and a #include statement in a C source. The path for searching for these statements is: Look in any directories specified by the -Idir option to jbc. If more than one -Idir option is used, left to right searching is used. Look in the directory $JBCRELEASEDIR/include. If the environment variable JBCRELEASEDIR is not set, look in directory /usr/jbc/include. If a jBC source, the current working directory is next searched. If a C source, the remaining search paths are exactly as for the cc command.

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.

Environment Variables and the jbc Command


The use of the -options to the jbc program is the main mechanism for controlling the work of the jbc program. There are a number of additional environment variables that may be used for controlling the jbc program. CC Setting this environment variable to the name of a program causes us to use the command defined by the CC variable instead of the default cc command. JBCLIBDIR You can specify new library directories to search by using this environment variable. These directories have the highest precedence. For example: % export JBCLIBDIR=-L /home/test/lib -L/home/libdir

jbc and BASIC Manual

10-6

Advanced Programmers Reference

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.

jpp Macro Pre-processor


The compiler also incorporates a sophisticated macro pre-processor that provides complex conditional compilation facilities. It supports compiler directives compatible with C compilers and BASIC compilers. A complete description of pre-processor directives follows. EQUATE EQUATE symbol TO substitute

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

Advanced Programmers Reference Manual

10-7

jbc and BASIC

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.

jbc and BASIC Manual

10-8

Advanced Programmers Reference

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.

Using the BASIC Command


The full syntax of the BASIC command is: BASIC {-v} {-w[0-3]} {DICT} filename {recordkey | * } {(OC[W0-3]}) -v Verbose mode. The CATALOG command will display details of external commands being executed -w[0-3] Set the warning level to 0, 1, 2 or 3. See later. DICT Optional DICTionary specifier for the file name. filename Name of the file to get the sources from. Can be any file supported by jEDI, such as a hashed file or a UNIX directory. recordkey The list of sources to process. * Shows to process all sources in the file. (O) Optimise the code. No debugging information is generated with this option. This O option is synonymous with the (E) option. (E) Optimise the code. No debugging information is generated with this option. This E option is synonymous with the (O) option and is provided for backwards compatibility with legacy applications. (W0-3) Set warning level to 0, 1, 2 or 3. See later If both recordkey and * are omitted, BASIC will use any preceding select list. If the BASIC command has no select list, it will use all records in the specified file. When processing the list of record keys, the BASIC command will ignore any $ prefixes and .o suffixes and build up a list of source names. Using this mechanism, you can safely use BASIC on all records in a file, and it will only process the sources you intended.

Advanced Programmers Reference Manual

10-9

jbc and BASIC

jbc and BASIC Warning Levels


The jbc and BASIC commands allows 4 levels of warning to be generated. The BASIC command uses the -wn option or the (Wn) option. For example, to set warning level 3 use either of the following commands: % BASIC -w3 FILENAME SOURCENAME % BASIC FILENAME SOURCENAME \(W3 The jbc command uses the -JCwn option, where n is the warning level from 0 to 3. For example, to set warning level 3 use something like the following: % jbc -Jcw3 FILENAME.b -c The default level is level 1. The four levels are: Level 0 1 Types of warning reported. None. No warnings generated at all. Reasonable. Only reports those things that are quite likely to be an error and really should be looked at. There are few of these. This is the default warning level. Pedantic. Reports all of the Reasonable errors plus a few more that are bad practice and should really be seen to. Highly Pedantic. Reports every little transgression of the rules.

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

jbc and BASIC Manual

10-10

Advanced Programmers Reference

Chapter 11: jBuildSLIb and CATALOG

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.

Advanced Programmers Reference Manual

11-1

jBuildSLib and CATALOG

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.

jBuildSLib and CATALOG Manual

11-2

Advanced Programmers Reference

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

Creating the Input Objects to jBuildSLib


This stage is quite simple, and just involves converting the source code into object code. Example Compile sources func1.c, func2.c and func3.c into archive library libfuncs.a: % cc -c func1.c func2.c func3.c % ar -rv libfuncs.a func1.o func2.o func3.o or % jbc -Jalibfuncs.a func[1-3].c Example Compile sources sub1.b and sub2.b, leaving them as object files: % jbc -c sub[12].b To summarise our rolling example, following the above steps we have these significant UNIX files: libfuncs.a contains the objects func1.o, func2.o and func3.o. libcomms.a - existing comms library. sub1.o and sub2.o - the jBC subroutines. func4.o - existing pre-compiled object.

Using the jBuildSLib Command to Create a Shared Object


Given objects in the form of .o files, archive libraries or existing shared objects, you would now create a shared object using the inputs. In our rolling example, we will create two shared objects, one called libfuncs.so (with an export list called libfuncs.so.el) and one called libutils.so (with an export list called libutils.so.el). Example Create the libfuncs.so from three input files: % jBuildSLib -o libfuncs.so libfuncs.a libcomms.a sub1.o Example Create the libutils.so from two input files. % jBuildSLib -o libutils.so sub2.o func4.o To summarise our rolling example, following the above steps we have these significant UNIX files: libfuncs.so contains the objects func1.o, func2.o, func3.o, libcomms.a and sub1.o. libutils.so contains the objects sub2.o and func4.o

Advanced Programmers Reference Manual

11-3

jBuildSLib and CATALOG

Referencing a Shared Object from a C or jBC Program


There are two ways a program can call functions inside a shared object: 1. Through a standard C function call. This is done inside a jBC program with a defined C function call, or through the CALL statement compiled with the -JLS flag. In these cases, the function must be visible when the executable is created, and the shared object must be available to the UNIX dynamic linker when the program starts up. In our example, the program prog1.b will work this way. Through the CALL statement. If we assume the jBC program had a CALL statement, but was not compiled with the -JLS flag, the binding of the function to the jBC program is done entirely dynamically at run-time. In fact, the function need not even be written when the jBC source is originally compiled. In our example, the program prog2.b will work this way.

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

jBuildSLib and CATALOG Manual

11-4

Advanced Programmers Reference

% jbc prog2.b -o prog2 func4.o % prog2

Advanced Programmers Reference Manual

11-5

jBuildSLib and CATALOG

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.

CATALOGing a jBC Program


These are the steps when a command such as: % CATALOG BP PROGRAM1 is executed: Any leading $ and trailing .o are removed from the name. A check is made for duplicate names - in the following command only one object would be processed: % CATALOG BP $PROGRAM1 PROGRAM1 PROGRAM1.o The $PROGRAM1 or PROGRAM1.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 program, rather than a subroutine. The directory $HOME/bin is created, if it doesnt already exist. The jbc command is invoked to create an executable program in directory $HOME/bin, something like this: % jbc BASIC_xx.o -o $HOME/bin/PROGRAM1 The CATALOG command cleans up any scratch files created.

CATALOGing a jBC Subroutine


These are the steps when a command such as: % CATALOG BP sub1.o is executed: Any leading $ and trailing .o are removed from the name.
jBuildSLib and CATALOG Manual Advanced Programmers Reference

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.

Extending the Use of CATALOG


The full syntax of the CATALOG command is: CATALOG {-v} {-obindir} {-Llibdir} {DICT} filename {recordkey | * } -v Verbose mode. The CATALOG command will display details of external commands being executed -obindir If you are cataloging a program, the output directory will be bindir instead of the default $HOME/bin. -Llibdir If you are cataloging a subroutine, the output directory will be libdir instead of the default $HOME/lib. DICT Optional DICTionary specifier for the file name. filename Name of the file to get the objects from. Can be any file type supported by jEDI, such as a hashed file or a UNIX directory. recordkey The list of objects to process. * Process all objects in the file. If both recordkey and * are omitted, CATALOG will use any preceding select list. If the CATALOG command has no select list, it will use all records in the specified file. When processing the list of record keys, the CATALOG command will ignore any $ prefixes and .o suffixes and build up a list of object names. It then removes duplicate names from its list. Finally, it looks for an object with a $ prefix, and failing that, it looks for an object with a .o suffix. Using this mechanism, you can safely use CATALOG on all records in a file - it will only process the objects you intend. Instead of the -obindir option, you can set the JBCDEV_BIN environment variable. Similarly, instead of the Llibdir option you can set the JBCDEV_LIB environment variable. For example, let us assume you dont want subroutines to be placed in $HOME/lib, but in directory /home/commonlib. There are two ways to achieve this: 1 Use the -Llibdir option. % CATALOG -L/home/commonlib BP sub1.o 2 Use the JBCDEV_LIB environment variable.

Advanced Programmers Reference Manual

11-7

jBuildSLib and CATALOG

% 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.

CATALOG Configuration File


When the CATALOG command is executed against one or more subroutines, it will use a file $HOME/lib/jLibDefinition to obtain some details about the way CATALOG is to be used. If the file doesnt exist, a default one will be created using $JBCRELEASEDIR/config/jLibDefinition as a template. If the -Llibdir option is used, or the JBCDEV_LIB environment variable is set, the jLibDefinition file in that directory will be used. This jLibDefinition definition file gives the CATALOG command information about the following: The format of the shared object library name it creates. The format of the export list associated with the shared object library name it creates. The name of the directory where the archive objects are placed prior to creating a shared object from them. The approximate maximum size of a shared object.

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!).

jBuildSLib and CATALOG Manual

11-8

Advanced Programmers Reference

Chapter 12: Advanced Tools

Introduction
This chapter describes some of the advanced tools and techniques that may be used to run and debug your program.

Advanced Programmers Reference Manual

12-1

Advanced Tools

jBC Run-time Options


A number of options may follow the invocation of a jBC program. The command line format is: progname { -Joption(s) {-Joption(s)} progparam { progparam..} Options available are: -Jb -Jc -Jd -JD Run as a background process. A background port number will be allocated from the available list (see jPML in the Systems Administrators Manual) and control will return to the calling program. To check the variables on exiting the program. This may be useful for debugging code with userwritten C function when looking for corrupted variables. To enter the debugger before execution of the first program line. The debugger prompt is displayed immediately. Same as the -Jd option, to immediately enter the debugger, but will set the JBCDEBUGGER environment variable so that all programs executed by the parent will also immediately enter the debugger. Normally the debugger will try to display the line of source code associated with the current program position. The debugger will, by default, only look in the current working directory for the source file. You can use the -Jp option to give a list of file names to search in, each name delimited by a colon. The file can be any supported by jEDI, including hashed files and UNIX directories. The following example tells the debugger to look first in the file BP in the home directory and then in the current working directory: % prog -Jp$HOME/BP:. You can also use the p command from the debugger prompt to achieve the same effect. -Jr Redirects debugger input/output to the specified file. This allows you to debug on a different terminal from that running the application. This is very useful when debugging applications where screen layout is important. You can also use the r command from the debugger prompt to achieve the same effect. To display the CPU usage on exiting the program. To set the terminal output to be unbuffered. Under normal circumstances, data printed to the terminal will only be actually be displayed under the following circumstances: A new line character is printed. The maximum number of characters have been buffered. The program terminates, or calls another program through the PERFORM, EXECUTE, CHAIN or ENTER command. The program pauses due to execution of an INPUT, SLEEP or RQM statement. For example, in the following code, the data is not actually printed until the very last line: FOR Loop = 1 TO 10 PRINT @(0,23):Loop :Loop:@(-4): CALL DOTHIS NEXT Loop PRINT Completed Some legacy applications do this sort of thing, and the status message is not displayed when it is required. There are a number of ways to force each PRINT or CRT statement to be displayed: Use this -Ju option. Append a CHAR(0) to the end of the string. For example PRINT @(0,23):Loop :Loop:@(-4):CHAR(0):

-Jp

-Js -Ju

Advanced Tools Manual

12-2

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

Advanced Tools Manual

12-4

Advanced Programmers Reference

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.

Reporting the Profiling Files


The profiling files can be reported with the 'jprof' command. If you simply enter 'jprof' you get the help screen: Called as : jprof {-n} {-i} {-fFilename {-fFilename}} jprof_nnn Where : -n Additionally sort by name -i Sort by increasing ticks, rather than decreasing tick. -fName Name of file to extract source from. prof_nnn Profile name A typical use of jprof would be: % jprof -f$HOME/BP -f. ./jprof This would produce a report on the file jprof, which was created by a previous jBASE program run with the -JP option. When running the report, it will attempt to display the source code associated with the lines, first trying file $HOME/BP and then the current working directory. The -i option can be used so the report is given with the least used lines of code first, and the most used lines of code at the end of the report.

Advanced Programmers Reference Manual

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.

Advanced Tools Manual

12-6

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

12-7

Advanced Tools

Debugging - the JBCDEBUGGER Variable


The debugger is normally entered in a variety of ways: The -Jd option can be used at the command line. The application can execute a DEBUG statement. A run-time error or warning might be detected by the library code.

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.

Advanced Tools Manual

12-8

Advanced Programmers Reference

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.

Advanced Programmers Reference Manual

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

Advanced Programmers Reference Manual

12-11

Advanced Tools

Extended terminfo Capabilities


The ability exists to add extra terminal and printer independent control characters, available to a jBC program using the @() function. These extensions only apply when the jBC source has been compiled using ROS emulation (by setting the UNIX environment variable JBCEMULATE to ROS). The standard @(-nn) function utilises entries defined in the UNIX terminfo database for operations such as clear to end of line, or cursor positioning. However, the extensions to the @(-nn) functionality do not map easily to the UNIX terminfo database. Therefore, a jBASE utility called 'jtic' exists which will take a description of the terminal and printer capabilities, and create a compiled binary that can be accessed by jBC programs, through the @(-nn) functions. This program also allows jBASE to work with operating systems that do not support UNIX style terminfo capabilities, such as Windows NT. Usage of the jtic program, and the format of the description file are very similar to that of the UNIX tic program.

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.

jtic Description File


To run jtic you need a description file to describe the capabilities. This file is very similar to that required by the UNIX tic command. It has the following format: Comment. Comment lines start with # as the first character of each line. Blank lines are also counted as comment lines. Example: # This is just a comment. Terminal names. This tells jtic the names of the terminal definition. It is a list of names delimited by | characters. The final field is treated as a comment. Following the terminal names will be the binary definitions, integer values and string values for those terminals. When another set of terminal names are defined, the current definitions are written to file and the current definitions nulled. Example: vt100|vt100am|prism-ans|Simple vt100 support Binary definition. These are just the name of the definition. Example:
Advanced Tools Manual 12-12 Advanced Programmers Reference

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.

Examples of Using jtic


This example shows how, on a UNIX machine, you can get a description file of your current capabilities using infocmp, and then use jtic to re-compile that definition. This is for example purposes only, and is actually meaningless in the UNIX environment as jBASE uses the UNIX terminfo database for normal terminfo definitions, rather than the binary that jtic produces. % infocmp vt100 > vt100.tic % jtic -v vt100.tic /usr/lib/terminfo/v/vt100_jbase /usr/lib/terminfo/v/vt100-am_jbase The next example shows a description file with extended capabilities, how to compile the file, and how to access it using a jBC program. Note the use of the -x option on jtic to create extended capabilities, and the setting of the JBCEMULATE variable before compiling the source. % cat lj.tic lj1|lj2| Laser jet stations. # if_prtr_letter=\E[02l, if_prtr_utray=\E[90x, % jtic -xv lj.tic /usr/lib/terminfo/l/lj1_ext_jbase /usr/lib/terminfo/l/lj2_ext_jbase % cat prog1.b EXECUTE "TERM lj1" PRINT "@(-70), if_prpt_letter, = " : SQUOTE(OCONV(@(-70),"MCPU")) PRINT "@(-76), if_prpt_utray , = " : SQUOTE(OCONV(@(-76),"MCPU")) % JBCEMULATE=ROS jbc prog1.b -Jo % ./prog1 @(-70), if_prpt_letter, = '^[[02l' @(-76), if_prpt_utray , = '^[[90x'

Extended Capabilities
The table below shows the extended capabilities.

Advanced Programmers Reference Manual

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 )

Advanced Tools Manual

12-14

Advanced Programmers Reference

@(-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 )

Advanced Programmers Reference Manual

12-15

Advanced Tools

@(-126)

if_prtr_status

Retrieve slave/printer device status

Advanced Tools Manual

12-16

Advanced Programmers Reference

Run-time Error Messages


jBASE contains a file of error messages which are displayed under the following circumstances: Using the PRINTERR statement in a jBASE program. Using the STOP or ABORT statement in a jBASE program. Using the PRINT-ERR command. The jBC run-time detects an error or warning. The jBC run-time uses the file as the source of textual information to display in certain utilities.

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.

Modifying Entries in the Error Message File


You can modify the entries in the error message file to suit your own particular requirements. For example, let us assume you have the following program: 001 PRINT A : B When the program is compiled and executed, you will get a run-time error caused by the variables A and B not having been initialised, and the debugger will be entered. The message will look something like this: Invalid or uninitialised variable -- ZERO USED, Var A, Line 1, source test1.b Trap from error message, message name = ZERO_USED

Advanced Programmers Reference Manual

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.

Advanced Tools Manual

12-18

Advanced Programmers Reference

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

Advanced Programmers Reference Manual

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

Advanced Programmers Reference Manual

21

Index

WRITE, 6-7, 7-1, 7-3, 7-9, 7-12, 7-24, 8-1, 12-6

Index Manual

22

Advanced Programmers Reference