___________________________________________________________



User Defined Functions (UDF's) for VAX Datatrieve

Donald E. Stern, Jr. - Warner Lambert Co., Milford CT
___________________________________________________________



Introduction

VAX Datatrieve provides a mechanism for easily extending the functionality  
provided in the "vanilla" product.  This additional functionality comes in the 
form of User Defined Functions or UDF's.  This article will deal with two major 
topics.  A system for creating and maintaining UDF's will be discussed first.  
Following this discussion, several specific UDF's, which have been added at our 
site, will be presented and discussed.


Creating and Maintaining UDF's

User Defined Functions are linked into the Datatrieve shareable image, DTRSHR. 
Several functions are already linked with DTRSHR when the product is installed.  
These functions include FN$ABS, FN$COS, FN$DAY, FN$LOG, FN$SHOW_TIMER, and 
FN$TRANS_LOG to name a few.  UDF's serve a wide variety of purposes.  Some 
functions are used in value expressions while others are used to modify the 
execution environment of a process.  

The functions linked with the Datatrieve image are specified in a Macro file 
called DFNFND.MAR.  The code in this file provides information regarding the 
name Datatrieve will use for this function, the name of the routine which will 
be linked with the image and called when the function is used, information 
regarding input and output arguments, etc.  The VAX DATATRIEVE Guide to 
Programming and Customization gives a fairly thorough explanation of what the 
various components of this file are and how to modify this file in order to add 
functions.

The specific routines called when the various functions are invoked can be a Run 
Time Library (RTL) routine, a System Service, or a user-written routine.  
Although extending Datatrieve by adding UDF's is a fairly straightforward 
procedure, many people are discouraged from doing so because of the need to deal 
with Macro, the linker, etc.  In a Wombat Magic presentation several years ago 
(Spring 1984) Phil Naecker described a process by which Datatrieve itself could 
be used to maintain the file DTRFND.MAR.  In fact, it was pointed out that the 
VAX Datatrieve development team uses a similar method to maintain the file which 
is ultimately shipped to customers.  We have implemented a system to create and 
maintain Datatrieve UDF's based upon this concept.  This system is described in 
the following paragraphs.


UDF Database Description

There is a one-to-many relationship between a function's attributes (DTR name, 
external name, value, query header, etc.) and its arguments.  A function can 
have 0 to n arguments, each having its own set of attributes (datatype, order, 
etc.)  Two domains have been created to contain the information needed to fully 
specify a UDF.  These domains are DTR_FUNCTION_INFO and DTR_FUNCTION_INPUTS and 
use the following record definitions.

DEFINE RECORD DTR_FUNCTION_INFO_REC USING
01 FUNCTION.
   03  DTR_FUNCTION_NAME           PIC X(31).
   03  FUNCTION_DESCRIPTION        PIC X(60).
   03  EXT_FUNCTION_NAME           PIC X(31).
   03  OUT_ARG_TYPE                PIC X(6)
       VALID IF (OUT_ARG_TYPE IN FUN_TYPE_TABLE).
   03  OUT_ARG_DTYPE               BYTE.
   03  OUT_ARG_DESCRIPTION         PIC X(60).
   03  EDIT_STR                    PIC X(31)
       MISSING VALUE " " QUERY_HEADER IS "EDIT STRING".
   03  QUERY_HDR                   PIC X(80)
       QUERY_NAME HDR MISSING VALUE " "
       QUERY_HEADER IS "QUERY HEADER".
   03  NOVALUE                     PIC X
       VALID IF NOVALUE EQ "Y","N" MISSING VALUE "N".
   03  NO_OPTIMIZE                 PIC X
       VALID IF NOVALUE EQ "Y","N" MISSING VALUE "N".
   03  IN_COUNT                    BYTE.
;


DEFINE RECORD DTR_FUNCTION_INPUTS_REC USING
01 INPUTS.
	03 DTR_FUNCTION_NAME  PIC X(31).
  	03 IN_ARG_TYPE        PIC X(5)
           VALID IF (IN_ARG_TYPE IN FUN_TYPE_TABLE).
  	03 IN_ARG_DESCRIPTION PIC X(60).
  	03 IN_ARG_DTYPE       BYTE.
  	03 ORDER              BYTE.
  	03 OUT_PUT            PIC X
           VALID IF OUT_PUT EQ "Y","N" MISSING VALUE "N".
  	03 ALL_LEN            WORD.
;

The fields used in these records are described in the following table.

     Field Name                           Description

Domain: DTR_FUNCTION_INFO

DTR_FUNCTION_NAME	     The name of the UDF.  This is the name that is used 
                             to reference the UDF from Datatrieve.

FUNCTION_DESCRIPTION	     This is a brief description of the functions 
                             intent.  The information is used to comment the 
                             MACRO code which is generated.

EXT_FUNCTION_NAME	     This is the name of the external function which 
                             will be called when the UDF is used.

OUT_ARG_TYPE		     This describes the value of the function.  It can 
                             be a value (eg. FN$LOG10(x)) or it can return a 
                             status.

OUT_ARG_DTYPE		     This is a code which specifies the datatype of the 
                             function.  A database of valid codes and 
                             corresponding datatypes is maintained.  (See 
                             Appendix IV for a listing of the domain, record, 
                             and table definitions, as well as, a listing of the 
                             valid datatypes.)

OUT_ARG_DESCRIPTION	     This describes the output and is used to comment 
                             the generated MACRO code.

EDIT_STR		     If a special edit-string is required, X(8) for 
                             example, this field is used to specify it.  It is 
                             not required information and can be left blank.

QUERY_HDR		     If a query header, other than the UDF name, is 
                             required then this field is used to specify it.

NOVALUE			     This field can contain a "Y" or an "N".  A "Y" 
                             indicates that no value is returned from the 
                             external function.  The UDFs FN$WIDTH and 
                             FN$CREATE_LOG are examples of such functions.

NO_OPTIMIZE		     This field can contain a "Y" or an "N".  If a "Y" 
                             is stored, then the UDF will not be optimized out 
                             of a loop.  The default value is "N".

IN_COUNT		     This specifies the number of arguments to be passed 
                             to/from the external function.  It corresponds 
                             exactly to the number of "member" records in 
                             DTR_FUNCTION_INPUTS.



Domain: DTR_FUNCTION_INPUTS

DTR_FUNCTION_NAME	     The DTR function name.  It relates records in this 
                             domain to records in DTR_FUNCTION_INFO.

IN_ARG_TYPE		     This field describes how the argument is passed, 
                             eg. by reference, by description, etc.

IN_ARG_DESCRIPTION	     This describes the argument.  The description is 
                             used to comment the generated MACRO code.

IN_ARG_DTYPE		     This describes the datatype of the argument.

ORDER			     This describes the order in which the argument 
                             appears in the argument list.

OUT_PUT			     Permitted values are "Y" and "N".  A "Y" indicates 
                             that this is a value returned from the external 
                             function rather than one passed to it.

ALL_LEN			     If the output is of predetermined length, it is 
                             specified here.  See FN$HEX and WL$OCTAL for 
                             examples.

Every UDF has one, and only one, record occurrence in the domain 
DTR_FUNCTION_INFO.  Each UDF must be identified by a unique function name, ie. 
FN$LOG10, this name is stored in the field DTR_FUNCTION_NAME.  Records in the 
domain DTR_FUNCTION_INPUTS contain information about a functions arguments.  
The field DTR_FUNCTION_NAME is used to relate records in this domain with the 
corresponding "owner" record in the DTR_FUNCTION_INFO domain.

Using the view definition given below the two domains are logically combined.  
Each record occurrence of this view contains sufficient information to 
completely define a UDF.

DEFINE DOMAIN DTR_FUNCTIONS OF DTR_FUNCTION_INFO,
   DTR_FUNCTION_INPUTS USING
01 DTR_FUNCTION OCCURS FOR DTR_FUNCTION_INFO.
       03 FUNCTION FROM DTR_FUNCTION_INFO.
       03 IN_ARGS OCCURS FOR DTR_FUNCTION_INPUTS WITH
                DTR_FUNCTION_NAME =
       	    	       DTR_FUNCTION_INFO.DTR_FUNCTION_NAME.
       	    05 INPUTS FROM DTR_FUNCTION_INPUTS.
;


As previously pointed out, UDF's reference external functions which are called 
and to/from which arguments are passed.  In the case of the User Defined 
Function FN$LOG10 the external function is the RTL routine MTH$ALOG10. The 
following shows the data stored for the Digital-supplied UDF FN$LOG10.

DTR_FUNCTION_NAME	    : FN$LOG10
FUNCTION_DESCRIPTION	    : Common logarithm
EXT_FUNCTION_NAME	    : MTH$ALOG10
OUT_ARG_TYPE		    : VALUE
OUT_ARG_DTYPE		    :   10
OUT_ARG_DESCRIPTION	    : output is a floating value in R0, R1
EDIT_STR		    :
QUERY_HDR		    :
NOVALUE			    : N
NO_OPTIMIZE		    : N
IN_COUNT		    :    1
   DTR_FUNCTION_NAME	           : FN$LOG10
   IN_ARG_TYPE		           : REF
   IN_ARG_DESCRIPTION	           : input is a floating value passed by reference
   IN_ARG_DTYPE		           :   10
   ORDER		           :    1
   OUT_PUT		           : N
   ALL_LEN		           :      0

The data shows us that the function FN$LOG10 uses the RTL function MTH$ALOG10; 
that the function returns a value which is a real number; no special 
EDIT_STRING or QUERY_HEADER are used; that the function can be optimized out 
of a loop (at Datatrieve compile time); and that the function requires a 
single argument, a real number passed by reference.


Maintaining the UDF Database

The procedures ADD_DTR_FUNCTION and ADD_DTR_FUNCTIONS were created in order to 
add data to the database.  The code for each is given in Appendix I.  Separate 
forms were created to capture the data for DTR_FUNCTION_INFO and 
DTR_FUNCTION_INPUTS.  While simplifying the data entry procedures, the existence 
of forms are not necessary; the procedures can be modified if the target system 
does not have FMS or TDMS installed. 

Two procedures, MAKE_DTRFND and PRINT_FUNCTION, are used to recreate the macro 
source file DTRFND.MAR.  The source code is assembled and linked into the 
Datatrieve shareable image. The code for these procedures is given in Appendix 
II.  MAKE_DTRFND, prints the Macro header information, executes PRINT_FUNCTION 
once for every record in DTR_FUNCTIONS, and prints the trailer information.  
PRINT_FUNCTION simply prints a DTR_FUNCTIONS record in the required format.  
Appendix II also contains the procedure MAKE_COMMAND which produces a DCL 
command file which, when executed, assembles DTRFND, copies the source, listing, 
and object files into a central location, and updates the library DTRFUN.OLB.

Since DTRFND.MAR is completely recreated by the use of these procedures, it is 
necessary to load data for Digital-supplied UDF's into the database as well as 
any site-specific UDF's.  


Site Specific UDF's


Naming Convention

We have developed a standard set of UDF's which are linked with the shareable 
Datatrieve images on each of the nodes in our network.  Almost without 
exception, UDF's previously described by users, in this and other 
publications, have followed the Digital naming convention.

                                    FN$name

We have found it more convenient to name our UDF's using a different 
convention.  All of our site specific UDF's are named using

                                    WL$name

as the convention.  By doing this, we can easily identify and separate Warner 
Lambert functions from those supplied by Digital.  Additionally, we are able 
to avoid naming conflicts as new versions of Datatrieve (with new functions) 
are released.  This significantly reduces the maintenance effort needed when 
re-installing Datatrieve.  The following is a list of some of the UDF's which 
have been implemented at our site.

Function Name                Function Description

WL$CHAR		     Convert an ASCII Code to an ASCII Character
WL$DAY		     Returns the number of days since the base date 17-Nov-1858
WL$DAY_OF_WEEK	     Returns a numeric day of the week for a date/time
WL$DELETE_FILE	     Delete a file
WL$DELETE_LOGICAL     Deletes a supervisor-mode logical from a specified table
WL$EDT		     Edit an ASCII file
WL$GET_SYMBOL	     Returns the value of a CLI symbol as a string
WL$OCTAL	     Convert an unsigned integer longword to an octal char. 
                     strg.
WL$POWER	     Computes the value of a number raised to a power
WL$RANDOM	     A random number generator
WL$RENAME_FILE	     Renames a file
WL$SET_LOGICAL	     (Re)defines a supervisor mode logical in a specified table
WL$SET_SYMBOL	     Define or redefine a CLI symbol
WL$WAIT		     Places current process in hibernation for specified time


WL$CHAR

We use this function primarily to generate non-printing ASCII characters, such 
as BELL (ASCII code 7) and ESCAPE (ASCII code 27), which tend to disrupt the 
printing of procedures when the character itself is embedded in the code.  In 
the Fall 1986 Wombat Magic session held in San Francisco, Pat Scopelletti 
demonstrated how the escape character could be programmed into a procedure 
using this function (Pat named his UDF FN$CHAR).

DECLARE ESC COMPUTED BY FN$CHAR(27).
	      :
	      :
PRINT ESC|"[?5i"     !Printer on
	      :
	      :
PRINT ESC|"[?4i"     !Printer off


WL$DAY_OF_WEEK

This function has a single argument, a DATE variable, and returns a value 
between 1 and 7 (Monday = 1 and Sunday = 7).  For example:

DTR> PRINT "TODAY" USING WWWWWWWWW, -
CON> "TODAY" USING DD-MMM-YYYY, WL$DAY_OF_WEEK("TODAY")

			WL$DAY
			   OF
			 WEEK

Sunday	   8-Feb-1987		7


WL$DELETE_FILE

This function has a single argument, a TEXT variable, which contains a valid 
file specification.  Unlike the DCL DELETE command, the file specification 
provided to this function does not require version number information.  For 
example:
                       DTR> WL$DELETE_FILE("YACHTS.DAT")

will delete the latest version of YACHTS.DAT.

This function is very useful in procedures which contain a DEFINE FILE 
command.  The UDF can be used to clear out any older versions of the file and 
thereby save disk space and the need to do this housekeeping outside of 
Datatrieve.


WL$DELETE_LOGICAL / WL$SET_LOGICAL

The Digital UDF FN$CREATE_LOG is used to create User Mode logical names.  
These functions are used to delete/create logical names in any specified 
logical name table (to which the user has write access of course.)  We found 
this particularly useful when spawning a subprocess from Datatrieve.  User 
mode logical names will not be defined for the subprocess but Process Mode 
logical names will.  For example, the statement

	   DTR> WL$SET_LOGICAL("DTR$LOGICAL","My logical name", 
                "LNM$PROCESS_TABLE")

will create a logical name called DTR$LOGICAL in the process logical name table 
and assign the value "My logical name" to it.  Similarly, the statement

           DTR> WL$DELETE_LOGICAL("DTR$LOGICAL","LNM$PROCESS_TABLE")

will delete the logical name from the table.


WL$EDT

This UDF requires a single argument, a TEXT variable containing a valid file 
specification.  It uses callable EDT to create/edit an ASCII text file.  In 
order to incorporate this function into the Datatrieve shareable image, we had 
to add a reference to the EDTSHR shareable image in the link command for DTRSHR.

            cluster=edtrtl,,,sys$common:[syslib]edtshr.exe/shareable

This UDF can be used in Datatrieve procedures to create and maintain ASCII 
files.  In this implementation, only one argument is used however the RTL 
function which is referenced will take a number of optional arguments as well 
(an EDT initialization file, for example).  The UDF could be restructured to 
accommodate these optional arguments as well.

One could achieve the same end result using an expression such as

                        DTR> FN$DCL("EDIT/EDT filespec")

but would incur the overhead involved with spawning a subprocess and activating 
the EDT.EXE image.  Using this function

                             DTR> WL$EDT(filespec)

will result in much less overhead and faster response time.


WL$GET_SYMBOL / WL$SET_SYMBOL

This pair of functions permit one to translate or define a symbol in the symbol 
table.  This can be particularly useful if Datatrieve is executing inside of a 
command or batch procedure.


WL$OCTAL

Modeled after DEC's FN$HEX function, WL$OCTAL permits the user to translate a 
decimal value into a string representing the octal equivalent.  This can be 
particularly useful when dealing with UIC's.

     DTR> PRINT WL$OCTAL(16)

     WL$OCTAL

           20


WL$POWER

This function simply raises a value to a power.  Two real number arguments are 
required, the base and the exponent.

DTR> PRINT WL$POWER(4.0,0.5), WL$POWER(5,3)

  WL$POWER	WL$POWER

  2.0000E+00	1.2500E+02


WL$RANDOM

This function returns a random number.  A single argument which is a longword 
"seed" value is required.  The RTL function MTH$RANDOM then computes a random 
value.  The function can be useful in statistical calculations requiring many 
iterations to compute probabilities.


WL$RENAME_FILE

As the name implies, this function is used to rename files.  It requires two 
arguments, both TEXT variables, which contain the old and new file 
specifications respectively.

               DTR> WL$RENAME_FILE(old_file_spec, new_file_spec)


WL$WAIT

This function takes a single argument, a floating value representing a 
specified number of seconds.  The function will then hibernate the process for 
the specified time.

                      DTR> WL$WAIT(5)     !Wait 5 seconds

This function can be used in applications where several reports or plots are 
created sequential and displayed on the screen.  If placed between report or 
plot statements, a pre-defined viewing time can be programmed into the 
procedure.


Generating Function Help

Digital supplies help text for all of the UDF's that are supplied with the 
installation kit.  By insuring that UDF's not supplied by Digital can be 
identified (via our naming convention), we are able to automatically generate 
help file source text for these functions.  The following procedure generates 
help file source text for our site-specific UDF's.

DEFINE PROCEDURE MAKE_FUNCTION_HELP
!
! This procedure generates help file source text for Warner Lambert
! UDF's.
!
! Written by:  Donald E. Stern, Jr.
!
READY DTR_FUNCTIONS SHARED
!
ON WL_FUNCTIONS.HLP
!
FOR DTR_FUNCTIONS WITH DTR_FUNCTION_NAME STARTING "WL"
BEGIN     ! Print the comments about the function
       PRINT "2  "|DTR_FUNCTION_NAME, SKIP 2,
       FUNCTION_DESCRIPTION (-),SKIP 2,
       OUT_ARG_DESCRIPTION (-), SKIP 2,"Arguments:", SKIP 1,
       ALL "     "|IN_ARG_DESCRIPTION (-), SKIP OF IN_ARGS, SKIP
END
END-PROCEDURE

The following is excerpted from the file WL_FUNCTIONS.HLP which was generated 
using this procedure.

       	    :
       	    :

2  WL$DELETE_FILE

Delete a file

No output

Arguments:
            Input is a filename to delete

       	    :
       	    :

Using the following command one can extract Digital function help from the 
Datatrieve help library SYS$HELP:DTRHELP.HLB.

            $ LIB/HELP/EXT=FUNCTIONS SYS$HELP:DTRHELP DEC_FUNCTIONS

Our help (WL_FUNCTIONS.HLP) can be concatenated with the Digital function help
(DEC_FUNCTIONS.HLP) into a single file (FUNCTIONS.HLP) using the following DCL 
command.

         $ COPY/CONC DEC_FUNCTIONS.HLP,WL_FUNCTIONS.HLP FUNCTIONS.HLP

This new help text is used to update the Datatrieve help library by the 
following command.

                 $ LIB/LOG/HELP/REP SYS$HELP:DTRHELP FUNCTIONS


Summary

User Defined Functions are one way in which the functionality of VAX 
Datatrieve can be greatly extended.  One need not be a MACRO programmer to 
implement UDFs.  The "cookbook" procedure described in the Datatrieve 
documentation can be followed or a system such as the one described here can 
be implemented.


Appendix I - Data Entry Routines


DEFINE PROCEDURE ADD_DTR_FUNCTIONS
!
! This procedures readies the necessary domains and calls
! the ADD_DTR_FUNCTION procedure 1 or more times to add new
! definitions for Datatrieve User Defined Functions.
!
!     Written by:    Donald E. Stern, Jr.
!                    Warner Lambert Company
!                    10 Webster Road
!                    Milford, CT  06460
!
!
READY DTR_FUNCTION_INFO SHARED WRITE
READY DTR_FUNCTION_INPUTS SHARED WRITE
DECLARE MORE PIC X.
!
! Shorthand for where we put User Defined DTR stuff.
FN$CREATE_LOG("SCHICK$DTR","SCHICK$DISK:[DTRLIB]")
MORE = "Y"
WHILE MORE="Y" BEGIN
        :ADD_DTR_FUNCTION
        MORE=FN$UPCASE(*.MORE)
END
END-PROCEDURE


DEFINE PROCEDURE ADD_DTR_FUNCTION
!
! This procedure is used to update the DTR_FUNCTIONS.DAT
! data file which all the necessary data to construct the
! DTRFUN.MAR file needed to add User Defined Functions
! (UDF's) to Datatrieve.
!
! NOTE:  The domains DTR_FUNCTION_INFO and
!        DTR_FUNCTION_INPUTS must be readed for WRITE
!        access before invoking this procedure.
!
!
!     Written by:    Donald E. Stern, Jr.
!                    Warner Lambert Company
!                    10 Webster Road
!                    Milford, CT  06460
!
!
!Create variables to contain data passed back from FMS
!
DECLARE FNAME PIC X(31).
DECLARE NARG  BYTE.
DECLARE ARGNM BYTE.
!
ARGNM=0
!
! Shorthand for where all our UDF stuff is.
!
FN$CREATE_LOG("SCHICK$DTR","SCHICK$DISK:[DTRLIB]")
!
!       Loop to update DTR_FUNCTION_INFO (1 record/UDF)
BEGIN
     STORE DTR_FUNCTION_INFO USING DISPLAY_FORM
           DTR_FUNCTION_ADD IN
           SCHICK$DTR:DTR_FUNCTIONS RETRIEVE USING
        BEGIN
          FNAME                        = GET_FORM NAME
          DTR_FUNCTION_NAME            = FNAME
          FUNCTION_DESCRIPTION         = GET_FORM FUN_DESC
          EXT_FUNCTION_NAME            = GET_FORM EXTERNAL
          OUT_ARG_TYPE                 = GET_FORM OUT_TYPE
          OUT_ARG_DTYPE                = GET_FORM OUT_DTYPE
          OUT_ARG_DESCRIPTION          = GET_FORM OUT_DESC
          EDIT_STR                     = GET_FORM EDT_STR
          QUERY_HDR                    = GET_FORM QRY_HDR
          NOVALUE                      = GET_FORM NOVAL
          NO_OPTIMIZE                  = GET_FORM NOOPT
          NARG                         = GET_FORM NARGS
          IN_COUNT                     = NARG
        END
     REPEAT NARG     !Loop to update DTR_FUNCTION_INPUTS
        BEGIN        !Contains 0-n records/UDF
          ARGNM = ARGNM + 1
          STORE DTR_FUNCTION_INPUTS USING DISPLAY_FORM
            DTR_FUNCTION_ADD_INARGS IN
                  SCHICK$DTR:DTR_FUNCTIONS USING
            BEGIN
               PUT_FORM NAME        = FNAME
               PUT_FORM ARGNUM      = ARGNM
            END RETRIEVE USING
            BEGIN
               DTR_FUNCTION_NAME    = FNAME
               IN_ARG_TYPE          = GET_FORM IN_TYPE
               IN_ARG_DESCRIPTION   = GET_FORM IN_DESC
               IN_ARG_DTYPE         = GET_FORM IN_DTYPE
               ORDER                = GET_FORM ARG_ORDER
               OUT_PUT              = GET_FORM OUTPUT
               ALL_LEN              = GET_FORM ALLLEN
            END
        END
END
END-PROCEDURE




Appendix II - Routines to Create DTRFND.MAR File


DEFINE PROCEDURE MAKE_DTRFND
!
! This procedure re-creates the MACRO file DTRFND.MAR
!
!       Supplied by:    Philip A. Naecker
!                       Consulting Engineer
!                       Altadena, CA
!
READY DTR_FUNCTIONS SHARED
SET COLUMNS_PAGE = 132
ON DTRFND.MAR
 BEGIN
  PRINT -
  ".TITLE  DTRFND  VAX-11 Datatrieve Function Definitions",
  SKIP,
  ";+++++++++++++++++++++++++++++++++++++++++++++++++++",
  SKIP, ";", SKIP,
  ";     F U N C T I O N    D E F I N I T I O N S", SKIP,
  ";", SKIP,
  ";---------------------------------------------------",
  SKIP 2,
  ".PSECT FND,NOWRT,SHR,PIC,2", SKIP 1,
  ".LIBRARY /DTR$LIBRARY:DTRFNLB/", SKIP 1,
  ".LIBRARY /SYS$LIBRARY:STARLET/", SKIP 1,
  ";.SHOW EXPANSIONS", SKIP 1,
  "$DSCDEF", SKIP 1,
  "$DTR$FUN_INIT", SKIP 4
!
!   Print all of the functions
!
  FOR DTR_FUNCTIONS  :PRINT_FUNCTION
!
!   Print the trailer
!
  PRINT "$DTR$FUN_FINI", SKIP 2, ".END"
 END;
END-PROCEDURE


DEFINE PROCEDURE PRINT_FUNCTION
!
! Print a single function definition in DTRFND.MAR
!
!       Supplied by:    Philip A. Naecker
!                       Consulting Engineer
!                       Altadena, CA
!
!     Modified by:     Donald E. Stern, Jr.
!                      Warner Lambert Company
!                      10 Webster Road
!                      Milford, CT  06460
!
BEGIN     ! Print the comments about the function
     PRINT "; "|DTR_FUNCTION_NAME|||"- "|
           FUNCTION_DESCRIPTION, SKIP, ";", SKIP,
           "; "|OUT_ARG_DESCRIPTION, SKIP,
           ALL "; "|IN_ARG_DESCRIPTION, SKIP OF IN_ARGS,
              SKIP
     ! The opening line
     PRINT "$DTR$FUN_DEF"|||DTR_FUNCTION_NAME||
          ", "|EXT_FUNCTION_NAME||", "|IN_COUNT
     ! The output argument
     PRINT "    $DTR$FUN_OUT_ARG  TYPE = "|
           OUT_ARG_TYPE VIA FUN_TYPE_TABLE||
           IF (OUT_ARG_TYPE EQ "STATUS","INPUT")
               THEN " "
           ELSE ", DTYPE = DSC$K_DTYPE_"|
               OUT_ARG_DTYPE VIA DTYPE_VALUE_TO_NAME
     ! If the function is a novalue function, say so.
     IF (NOVALUE EQ "Y") THEN
                PRINT "    $DTR$FUN_NOVALUE"
     ! If the function is a nooptimize function, say so
     IF (NO_OPTIMIZE EQ "Y") THEN
                PRINT "    $DTR$FUN_NOOPTIMIZE"
     ! Do the edit-string and query-header
     IF (EDIT_STR NOT MISSING) THEN
         PRINT "    $DTR$FUN_EDIT_STRING ^\"||
                EDIT_STR||"\"
     IF (HDR NOT MISSING) THEN
         PRINT "    $DTR$FUN_HEADER  HDR = <"||HDR||">"
     ! OK, now for the input arguments
     FOR A IN IN_ARGS
          CHOICE
            (IN_ARG_TYPE = "TEXT") THEN
               PRINT "    $DTR$FUN_IN_ARG  TYPE ="|||
               (IN_ARG_TYPE VIA FUN_TYPE_TABLE)||
               (IF (ALL_LEN EQ 0) THEN " "
                     ELSE ",ALL_LEN = "|ALL_LEN)|
               (CHOICE
                  (ORDER NE 0) THEN
                                ", ORDER = "|ORDER
                  (OUT_PUT EQ "Y") THEN
                                ", OUT_PUT = TRUE"
                          ELSE
                             ";;; illegal OUTPUT or ORDER"
                 END_CHOICE)
            (IN_ARG_TYPE = "VALUE") THEN
               PRINT "    $DTR$FUN_IN_ARG  TYPE ="|||
               (IN_ARG_TYPE VIA FUN_TYPE_TABLE)||
               ", DTYPE = DSC$K_DTYPE_"|
               IN_ARG_DTYPE VIA
                        DTYPE_VALUE_TO_NAME||", ORDER = "|
                        ORDER
            (IN_ARG_TYPE = "NULL") THEN
               PRINT "    $DTR$FUN_IN_ARG  TYPE ="|||
               (IN_ARG_TYPE VIA FUN_TYPE_TABLE)
            (IN_ARG_TYPE = "REF", "DESC") THEN
               PRINT "    $DTR$FUN_IN_ARG  TYPE ="|||
               (IN_ARG_TYPE VIA FUN_TYPE_TABLE)||
               ", DTYPE = DSC$K_DTYPE_"|
               IN_ARG_DTYPE VIA
                        DTYPE_VALUE_TO_NAME||
               (CHOICE
                  (ORDER NE 0) THEN ", ORDER = "|
                            ORDER
                  (OUT_PUT EQ "Y") THEN
                               ", OUT_PUT = TRUE"
                ELSE ";;; Illegal OUTPUT or ORDER"
               END_CHOICE)
              ELSE PRINT ";;; Illegal input type"
          END_CHOICE
     PRINT "$DTR$FUN_END_DEF", SKIP
END
END-PROCEDURE

DEFINE PROCEDURE MAKE_COMMAND_FILE
   ON CHECK.COM
   BEGIN
   PRINT "$MAC/LIS DTRFND"
   PRINT "$COPY DTRFND.MAR, DTRFND.LIS, DTRFND.OBJ -"
   PRINT "     SCHICK$DISK:[DTRLIB]*/LOG"
   PRINT "$LIBR/LOG/REP SCHICK$DISK:[SCHICK.DTRLIB]DTRFUN -"
   PRINT "     SCHICK$DISK:[DTRLIB]DTRFND"
   END
END-PROCEDURE




Appendix III - WL$x Function Definitions from DTRFND.MAR

The following was extracted from the file DTRFND.MAR.  It provides the precise 
definition for each of the site-specific UDF discussed above.

; WL$CHAR - convert an ASCII Code to an ASCII Character
;
; output is a one character string
;
; input is an unsigned byte ASCII code

$DTR$FUN_DEF WL$CHAR, LIB$CHAR, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_IN_ARG  TYPE = FUN$K_TEXT, OUT_PUT = TRUE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF,
    	 DTYPE = DSC$K_DTYPE_BU, ORDER = 1
$DTR$FUN_END_DEF

; WL$DAY - returns the number of days since the base date 17-Nov-1858
;
; output is a longword integer cont. the days since base date
;
; input is a binary quadword date

$DTR$FUN_DEF WL$DAY, LIB$DAY, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF,
    	 DTYPE = DSC$K_DTYPE_L, OUT_PUT = TRUE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF,
    	 DTYPE = DSC$K_DTYPE_ADT, ORDER = 1
$DTR$FUN_END_DEF

; WL$DAY_OF_WEEK - returns a numeric day of the week for a date/time
;
; output is an unsigned longword value between 1 and 7
; input is a 64-bit date/time
;

$DTR$FUN_DEF WL$DAY_OF_WEEK, LIB$DAY_OF_WEEK, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF,
    	 DTYPE = DSC$K_DTYPE_ADT, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF, 
    	 DTYPE = DSC$K_DTYPE_L, OUT_PUT = TRUE
$DTR$FUN_END_DEF

; WL$DELETE_FILE - Delet a file
;
; No output
; Input is a filename to delete

$DTR$FUN_DEF WL$DELETE_FILE, LIB$DELETE_FILE, 1
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_NOOPTIMIZE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
$DTR$FUN_END_DEF

; WL$DELETE_LOGICAL - deletes a supervisor-mode logical from a specified table
;
; no output
; argument 1 is a char. string containing the logical name
; arg. 2 is a char. string cont. the logical name table

$DTR$FUN_DEF WL$DELETE_LOGICAL, LIB$DELETE_LOGICAL, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_NOOPTIMIZE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 2
$DTR$FUN_END_DEF

; WL$EDT - Edit an ASCII file
;
; No output
; input is a filename passed by descriptor

$DTR$FUN_DEF WL$EDT, EDT$EDIT, 1
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_NOOPTIMIZE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
$DTR$FUN_END_DEF

; WL$GET_SYMBOL - returns the value of a CLI symbol as a string
;
; output is a character string passed by desc.
; arg. 1 is a char. string cont. the symbol name
;

$DTR$FUN_DEF WL$GET_SYMBOL, LIB$GET_SYMBOL, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_TEXT, OUT_PUT = TRUE
$DTR$FUN_END_DEF

; WL$OCTAL - convert an unsigned integer longword to an octal char. strg.
;
; output is a character string representing an octal value
; input is an unsigned integer longword passed by reference
;

$DTR$FUN_DEF WL$OCTAL, OTS$CVT_L_TO, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_EDIT_STRING ^\X(8)\
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF, 
    	 DTYPE = DSC$K_DTYPE_L, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_TEXT,
    	 ALL_LEN = 8, OUT_PUT = TRUE
$DTR$FUN_END_DEF

; WL$POWER - computes the value of a number raised to a power
;
; output is a real number
; argument 1 is the base
; argument 2 is a real exponent

$DTR$FUN_DEF WL$POWER, OTS$POWRR, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_VALUE, 
    	 DTYPE = DSC$K_DTYPE_F
    $DTR$FUN_IN_ARG  TYPE = FUN$K_VALUE, 
    	 DTYPE = DSC$K_DTYPE_F, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_VALUE, 
    	 DTYPE = DSC$K_DTYPE_F, ORDER = 2
$DTR$FUN_END_DEF

; WL$RANDOM - a random number generator
;
; output is an F-floating random number
; input is an unsigned integer longword

$DTR$FUN_DEF WL$RANDOM, MTH$RANDOM, 1
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_VALUE, 
    	 DTYPE = DSC$K_DTYPE_F
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF, 
    	 DTYPE = DSC$K_DTYPE_L, ORDER = 1
$DTR$FUN_END_DEF

; WL$RENAME_FILE - Renames a file
;
; No output
; Argument 1 is the source filename passed by descriptor
; Argument 2 is the new filename passed by descriptor

$DTR$FUN_DEF WL$RENAME_FILE, LIB$RENAME_FILE, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_NOOPTIMIZE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 2
$DTR$FUN_END_DEF

; WL$SET_LOGICAL - (re)defines a supervisor mode logical in a specified table
;
; no output
; arg. 1 is a char. string containing the logical name
; arg. 2 is a char string containing the value to assign
; arg. 3 is a char. string cont. the name of the table

$DTR$FUN_DEF WL$SET_LOGICAL, LIB$SET_LOGICAL, 3
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_NOOPTIMIZE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 2
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 3
$DTR$FUN_END_DEF

; WL$SET_SYMBOL - define or redefine a CLI symbol
;
; no output
; arg. 1 is a string descriptor containing the CLI symbol
; arg. 2 is a string descriptor cont. the CLI symbol value

$DTR$FUN_DEF WL$SET_SYMBOL, LIB$SET_SYMBOL, 2
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 1
    $DTR$FUN_IN_ARG  TYPE = FUN$K_DESC, 
    	 DTYPE = DSC$K_DTYPE_T, ORDER = 2
$DTR$FUN_END_DEF

; WL$WAIT - places current process in hibernation for specified time
;
; no output
; input is the number of seconds to wait - F-floating by ref.

$DTR$FUN_DEF WL$WAIT, LIB$WAIT, 1
    $DTR$FUN_OUT_ARG  TYPE = FUN$K_STATUS
    $DTR$FUN_NOVALUE
    $DTR$FUN_IN_ARG  TYPE = FUN$K_REF, 
    	 DTYPE = DSC$K_DTYPE_F, ORDER = 1
$DTR$FUN_END_DEF


Appendix IV - Database for Valid Datatypes

DEFINE TABLE DTYPE_NAME_TO_VALUE FROM DTYPS USING
	NAME : VALUE
END_TABLE

DEFINE TABLE DTYPE_VALUE_TO_NAME FROM DTYPS USING
	VALUE : NAME
END_TABLE

DEFINE DOMAIN DTYPS USING DTYP_REC ON
	SCHICK$DISK:[DTRLIB]DTYPE.DAT;

DEFINE RECORD DTYP_REC
01 DTYPE.
	03 VALUE	PIC 99.
	03 NAME 	PIC X(4).
	03 DESC 	PIC X(60).
;

Value   Name                Description

 00	 Z      unspecified
 01	 V      bit - an aligned bit string
 02	 BU      byte logical - 8-bit unsigned quantity
 03	 WU      word logical - 16-bit unsigned quantity
 04	 LU      longword logical - 32-bit unsigned quantity
 05	 QU      quadword logical - 64-bit unsigned quantity
 06	 B      byte integer - 8-bit signed 2's complement integer
 07	 W      word integer - 16-bit signed 2's-complement integer
 08	 L      longword integer - 32-bit signed 2's-complement integer
 09	 Q      quadword integer - 64-bit signed 2's-complement integer
 10	 F      F-floating - 32-bit single precision real number
 11	 D      D-floating - 64-bit double precision
 12	 FC      F-floating complex - low address real, high address imag
 13	 DC      D-floating complex- Pair of D-float. (low real, high imag.)
 14	 T      char. coded text - string data type
 15	 NU      numeric string, unsigned
 16	 NL      numeric string, left separate sign
 17	 NLO      numeric string, left overpunched sign
 18	 NR      numeric string, right separate sign
 19	 NRO      numeric string, right overpunched sign
 20	 NZ      numeric string, zoned sign
 21	 P      packed decimal string
 22	 ZI      sequence of instructions
 23	 ZEM      procedure entry mask
 24	 DSC      descriptor - levels of descriptors are allowed
 25	 OU      octaword logical - 128-bit unsigned quantity
 26	 O      octaword integer - 128-bit signed 2's complement integer
 27	 G      G-floating - 64-bit double precision number
 28	 H      H-floating - 128-bit quad precision real number
 29	 GC      G-floating complex- Pair of G-float. (low real, high imag.)
 30	 HC      H-floating complex- Pair of H-float. (low real, high imag.)
 31	 CIT      COBOL Interm. Temp. - 18-digit norm. dec. fract. 2-dig exp.
 32	 BPV      bound procedure value
 33	 BLV      bound label value
 34	 VU      bit unaligned - data are 0 to 2**16-1 contiguous bits
 35	 ADT      absolute date and time
 37	 VT      varying character-coded text


Appendix V - Table of Function/Argument Types

DEFINE TABLE FUN_TYPE_TABLE
	"STATUS"	:	"FUN$K_STATUS"
	"INPUT"		:	"FUN$K_INPUT"
	"VALUE"		:	"FUN$K_VALUE"
	"REF" 		:	"FUN$K_REF"
	"DESC"		:	"FUN$K_DESC"
	"TEXT"		:	"FUN$K_TEXT"
	"NULL"		:	"FUN$K_NULL"
END_TABLE