Developing Applications in DATATRIEVE
High performance Menus

Joe H. Gallagher, Ph. D.

Although DATATRIEVE was designed to be and is best suited as
an on-line, interactive, query and report writing language,
its lack of built-in menu capabilities has not stopped a
very large number of users from writing big menu-driven
application in DATATRIEVE.

There are basically four ways to create menu-driven applications in DATATRIEVE.

using one very large "pre-compiled" procedure

using a DCL menu

using callable DATATRIEVE, and

using logicals and a pseudo-recursive procedure.

These four ways of creating menus in DATATRIEVE have been
described in presentations by Chris Wool at the last three
DECUS Symposia entitled "Writing Menu-Drive Systems in
VAX-DATATRIEVE." A summary of the important features of
each method has appeared in the DTR/4GL Session Notes at
each Symposia. In addition, magic presentation by Bob
Hoover,1 Lorey
Kimmel,2 Gary
Burton,3 Pat
Scopelliti,4 Larry
Jasmann,5 and articles by Diane
Pinney using logicals with a pseudo-recursive
procedure,6 by Berrie Gray using a
very innovative approach to circumventing the poor
performance of "pre-compiled"
procedures,7 and by S. Begelman
using callable DATATRIEVE8
illustrate examples of these techniques.

It is not the purpose of this article to compare and
contrast the features and capabilities of these four
methods, but to describe an extension to one of these
methods which, I believe, makes the technique far superior
to all the others. The menu technique which I will fully
describe below is of the type which uses logicals and a
pseudo-recursive procedure, but it contains three important
extensions to the basic technique. These three extensions
are:

the control, prompt, security, and formatting information of the menu system
is actually data in a DATATRIEVE domain;

the menu system can be subdivided into a very, very
large system of sub-menus with essentially no performance
penalty; (The method of accomplishing this concept was
suggested to me by Tom Considine of Applied Video Systems at
the Nashville Symposia.)

the pseudo-recursion itself is accomplished by the use of a logical which is only
defined just before the pseudo-recursion is invoked.

MENU_SYSTEM: The values in the field MENU_SYSTEM are a two
character abbreviation for the menu or sub-menu and are used
to divide the menu system into easily manageable sub-menus.
This field would have values such as "MN" for the main menu,
"AR" for the accounts receivable sub-menu, "AP" for the
account payable sub-menu, "SR" for the supervisor reports
menu, etc. The information in this field (the value) is
never seen by the user, but is used by the main menu
procedure to determine which part or sub-part of the menu
system is currently to be accessed.

RECORD_TYPE: There are two types of records in the MENU
domain - header records and control records. RECORD_TYPE
has the value "0" for header records and "1" for control
records. Header records are used to create a menu header
or title for each menu. Control records contain the
control, security, and prompting information of each option
on the menu.

OPTION: The field OPTION controls the order in which header
records and control records are displayed on each menu.
They are also the selection criterion for choosing an item
from a menu.

MENU_GROUP_KEY: The group element, MENU_GROUP_KEY, is the
primary key of the indexed domain MENU. By using this group
element as a primary key, it is possible to avoid sorting
the records in order to display them of each menu. This
technique of using a group element as the primary key plays
a very important role in giving this implementation good
performance characteristics.

PROC_NAME: The field PROC_NAME is blank for header records.
For control records it contains the name of the DATATRIEVE
procedure which is to be executed when this particular menu
item is selected.

RIGHTS_OWNER: The field RIGHTS_OWNER contains a string which
is the name of the rights identifier which must be
possessed in order to be allowed to make this menu selection.
The importance of this field well be clarified when we
described the tables and procedures below.

DESCRIPTION: The field DESCRIPTION contains the text which
is displayed on the menu for both header and control
records. Note that for header records this string field may
contain escape sequences which activate terminal
characteristics such as double height/double width
characters.

The listing of two typical records in the MENU domain might look like:

The first record is a header record (RECORD_TYPE=0); it can
be accessed by those who have the "ALL" rights identifier,
and the DESCRIPTION field contains the first part (top part)
of a two part entry which makes a double height, double
width display of "Menu Data Management System". The second
record is a control record (RECORD_TYPE=1); it is menu
option 1 which activates the procedure MOVE_TO_SUPERS_MENU,
and is described with "Move to the Supervisor's Menu". The
MENU domain would contain one record for each menu option
plus one or two header records for each menu and submenu.

In addition to the domain MENU, there are two domain tables
which are used. These are as follows:

Before we can start the menu system, certain global
variables and logicals must be properly set up. The
LOGIN.COM file of the account or the SYLOGIN.COM file needs
to contain some DCL commands to establish the RIGHTS LIST
for the user. This would be accomplished with some DEC that
goes like:

The net result of this DCL code is to create a process
logical PROCESSRIGHTS which contains a list (separated by
spaces) of the rights that the user's process possesses.
The rights are granted to the user by the system manager
with the GRANT?IDENTIFIER command with the AUTHORIZE
utility. Determination of typical values for this logical
might look like:

The DATATRIEVE startup command file which is used to
automatically activate the menu system is as follows:

! dtrstartup.com
DECLARE ESC PIC X(1). ! This is going to hold the ESC character.
DECLARE CLEARIT PIC X(6) ! This going to hold the ESC sequence
QUERY-HEADER IS -. ! to clear a video screen with no header.
DECLARE BOTTOMIT PIC X(7) ! This is going to hold the ESC sequence
QUERY-HEADER IS -. ! to position the curser on line 22.
DECLARE RIGHTS_STRING PIC X(80).
! The rights list passed from DCL. If you have lots of rights, you may need
! more than 80 characters in the global variable.
DECLARE MENU_TYPE PIC XX.
! This is the variable which controls which menu or submenu is used. It is
! initially set by this startup file, but is changed by various procedures
! to move about.
DECLARE GROUP_SELECTION COMPUTED BY ! GROUP_SELECTION is used to
MENU_TYPE|"1"|FORMAT SELECTION USING 999 ! lookup entries in the bales
EDIT-STRING IS X(5).
DECLARE SELECTION PIC 99
VALID IF GROUP_SELECTION IN MENU_TABLE AND
RIGHTS_STRING CONTAINING GROUP_SELECTION VIA MENU_SECURITY_TABLE||" ".
! The complex validation clause on SELECTION assures us that menu selection
! entered is (first) a valid entry, and (second) an entry which this user
! is "privileged" (by holding rights list identifiers) to access the option.
DECLARE LOOP_PROCEDURE PIC X(8).
! A string which contains the name of the procedure which is to be called
! pseudo-recursively. Ususally this will be MAINLOOP, but it will change
! when it is time to exit and break the recursive loop.
ESC = "&ltESC&gt"
! The escape character. Use GOLD 27 GOLD 3 in the EDT editor to set this.
! [Note added in 1997: it would be better to use the function fn$char(27).]
CLEARIT = ESC|"[H"|ESC|"[J"
! The escape sequence to hame and clear a screen on VT100 and VT200 type terminals
BOTTOMIT = ESC|"[22;1H"
! The escape sequence to position the curser at the beginning of line 22.
:WORKING
! A procedure which is given below that put a blinking ". . . Working . . ."
! message on the screen to entertain the user while the rest of the
! startup command file is executed.
MENU_TYPE = "MN101" via MENU_TABLE
! A dummy line which forces the MENU-TABLE to be loaded so that there will be
! no delay later.
MENU_TYPE = "MN101" via MENU_SECURITY_TABLE
! A dummy line which forces the MENU-SECURITY-TABLe to be loaded so that there
! will be no delay later
MENU_TYPE = "MN" ! Initially points to the MaiN menu
RIGHTS_STRING = FN$TRANS_LOG("PROCESSRIGHTS")
! Move the rights list from the logical to a global variable. We will keep
! checking these rights and global will be faster than a logical.
LOOP_PROCEDURE = "MAINLOOP"
! Set the name of the procedure MAINLOOP in global variable.
FN$CREATE_LOG("MAINPROC",LOOP_PROCEDURE)
! The global variable is then moved to the logical
READY MENU SHARED READ
! Shared ready is essential for a multi-user menu system.
:MAINPROC
! Execute the main loop procedure. The logical MAINPROC is translated into
! MAINLOOP and then executed.
EXIT
! This exit may or may not be executed deponding on which type of "exit"
! procedure is activated instead of MAINLOOP.

This DATATRIEVE startup command file may appear to be
unnecessarily complicated. The complexity of having both
the global variable, LOOP_PROCEDURE, and the logical,
MAINPROC, is, in fact, necessary to allow the menu system
to be "gracefully" stopped and an exit to either the
DATATRIEVE prompt or DCL be made. This, I hope, will become
clear when we discuss the several exit procedures.

Now we have established enough of the environment that we
can understand what is going on in the main pseudo-recursive
procedure MAINLOOP. This procedure is as follows:

DEFINE PROCEDURE MAINLOOP
PRINT CLEARIT ! Clear the screen
PRINT DESCRIPTION(-) OF MENU WITH ! Print header lines for
MENU_GROUP_KEY STARTING WITH MENU_TYPE|"0" AND ! this menu
RIGHTS_STRING CONTAINING RIGHTS_OWNER||""
PRINT SKIP 2 ! Skip down a little bit
PRINT COL 10, OPTION(-) using Z9, " - ", ! Print all menu option
DESCRIPTION(-) USING T(55) OF MENU WITH ! lines for this menu
MENU_GROUP_KEY STARTING WITH MENU_TYPE|"1" AND ! that the user is allowed
RIGHTS_STRING CONTAINING RIGHTS_OWNER||"" ! to use.
PRINT BOTTOMIT ! Put curser on line 22.
SELECTION = *."selection from menu"
! Prompt user for selection. Remember the complex validation clause on
! selection assures us that the input is legal and that the user has the
! rights list privilege to choose the option
FN$CREATE_LOG("CURRPROC",GROUP_SELECTION VIA MENU_TABLE)
! Translate the users choice (by number) into the name of the procedure
! that is to be executed.
:CURRPROC ! Execute the procedure.
!
! Note that this procedure is not within a BEGIN-END loop. Therefore, any
! or all of the procedures so executed my contain DATATRIEVE command and
! statements such as READY, DEFINE, DEFINEP, DELETE, FIND, and SELECT
! which can not be used in large "pre-compiled" procedures with one large
! BEGIN-END loop. This has the advantage that one does not have to ready
! every possible domain that one might need. You can wait to ready a
! domain when (and only when and if) you need it.
!
FN$DELETE_LOG("MAINPROC") ! See discussion below.
FN$CREATE_LOG("MAINLOOP",LOOP_PROCEDURE)
:MAINPROC ! See discussion below.
END-PROCEDURE

Because the execution of MAINPROC (which is usually MAINLOOP
procedure) is the last statement in the procedure MAINLOOP,
the procedure is not actually calling itself recursively.
However, since the procedure does, in fact, include a
logical reference which is resolved to itself, procedures of
this type a called pseudo-recursive (they appear to be
recursive but really are not).

The last tree lines of the procedure (FN$DELETE_LOG,
FN$CREATE_LOG, and execute MAINPROC) are at the heart of the
pseudo-recursion. When the procedure MAINLOOP is invoked,
the logical MAINPROC will be evaluated if it exits. Because
the logical MAINPROC would not exist until after the
FN$CREAT_LOG has been performed, it forces the compiler in
DATATRIEVE toe-evaluate the logical MAINPROC each time it is
used. By this ruse, it is possible to have one or more
procedures which are called in the menu system which change
the contents of the global variable LOOP_PROCEDURE and thus
affect a "graceful" breaking of the infinite
pseudo-recursive loop.

The procedure to terminate the menu system and cause a
normal exit from DATATRIEVE is as follows:

DEFINE PROCEDURE STOP_MENU
LOOP_PROCEDURE = "EXITPROC"
END-PROCEDURE

All this procedure does is to change the contents of the
global variable LOOP_PROCEDURE. When the MAINLOOP procedure
deletes and creates the logical MAINPROC, the procedure will
not not point to MAINLOOP but EXITPROC. EXITPROC is as
follows:

DEFINE PROCEDURE EXITPROC
PRINT CLEARIT
END-PROCEDURE

Essentially all the procedure EXITPROC need do is clear the
screen since by not recursively call MAINLOOP the loop is
broken. Control then falls through to the last line of the
startup file DTRSTARTUP.COM which is the DATATRIEVE command
EXIT.

If one wants to break the loop and get to the DATATRIEVE prompt, one uses the procedure
EXIT_PROCEDURE which is:

Using the ABORT command will break all levels of the
procedures and will deliver the user to the "DTR&gt" prompt of
DATATRIEVE. Of course, one may not want a user in captured
accounts to have access to this procedure.

Movement through the menu system is accomplished by very
simple procedures which change the value of the global
variable, MENU_TYPE. Two examples of such procedures are as
follows:

DEFINE PROCEDURE MOVE_TO_SUPER_MENU
! A procedure to move to the supervisor's sub-menu
MENU_TYPE = "SU" ! SU as the current menu
END-PROCEDURE
DEFINE PROCEDURE RETURN_TO_MAINMENU
MENU_TYPE = "MN" ! set MN as the current menu again
END-PROCEDURE

There are several "utility" type procedures which are very
useful to have available in the menu system. The first of
these, WORKING, was used in the DTRSTARTUP.COM file to pacify
the user while the startup command file was executing. This
procedure is:

The procedure NOT_YET is used as a dummy procedure to hold a
place in procedure development. If one is going to write a
procedure whose name is FOO, but you haven't yet written it,
then you can put FOO in the menu system along with its
rights and description, but let FOO point to NOT_YET. This
is kink of top-down implementation of procedures in this
menu system. Note that NOT_YET contains a FN$DCL to the WAIT
DCL command. If you are handy with re-linking DATATRIEVE,
you may want to use a user define function like Don Stern's
WL$WAIT9 instead of FN$DCL since
it takes much less of the processor.

For menu system users who you wish to completely control,
one can make the following command file as their login file.

and the protection of the files [MANAGER]DTRSTARTUP.COM and
[MANAGER]CAPLOGIN.COM should be only read and execute (not
write or delete). With such an environment, there is pretty
good change that most users can not escape for the menu
system not can they do too much damage although it is still
possible for them to use PHONE, MAIL, and the editor.

There is one more procedure which I find very helpful. I
like the titles on my menus to be double height and double
width. The following procedure is used to create the two
header records for a new sub-menu. The procedure is
HEADER_LOAD.

What I have described is a complete. high-performance menu
system. It appears very complex and difficult to initially
set up. Bet the rewards of such a system pay off handsomely
in maintainability of the system. Each procedure of the
application is essentially independent of every other to the
application. The greatest benefit of such a system occurs
when the user needs a new function for the system. That
function is written and tested. It is converted into a
procedure. And then it is installed into the system by
adding a sing record tot he MENU domain. This enhancement,
of course, can be done while multiple users are
simultaneously accessing the menu system!

Originally published in the newsletter of the DATATRIEVE/4GL SIG,
The Wombat Examiner and 4GL Dispatch,
Volume 9, Number 3, pages 3-11; in the Combined SIGs Newsletters of
Digital Equipment Computer Users Society, Volume 3, Number 3,
December1987.