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