Optional GT.M Environment Translation Facility

For users who wish to dynamically (at run-time) determine a global directory from non-global directory information (typically UCI and VOL) in the environment specification, GT.M provides an interface to add an appropriate translation.

Using this facility impacts the performance of every global access that uses environment specification. Make sure you use it only when static determination of the global directory is not feasible. When used, make every effort to keep the translation routines very efficient.

The use of this facility is enabled by the definition of the logical GTM_ENV_TRANSLATE, which contains the path of a sharable image with the following entry point:

gtm_env_xlate

If the shared object is not accessible or the entry point is not accessible, GT.M reports an error.

The gtm_env_xlate() routine has the following C prototype:

        	int gtm_env_xlate(xc_string_t *in1, xc_st
            ring_t *in2, xc_string *in3, xc_string_t *out)
            

where xc_string_t is a structure defined in gtmxc_types.h as follows:

        typedef struct
        {
        int length;
        char *address;
        }xc_string_t;
            

The purpose of the function is to use its three input arguments to derive and return an output argument that can be used as an environment specification by GT.M. Note that the input values passed (in1, in2 and in3) are the result of M evaluation and must not be modified. The first two arguments are the expressions passed within the up-bars "| |" or the square-brackets "[ ]", and the third argument is the current working directory as described by $ZDIRECTORY.

A return value other than zero (0) indicates an error in translation, and is reported by a GT.M error

If the length of the output argument is non-zero, GT.M appends a secondary message of GTM-I-TEXT, containing the text found at the address of the output structure.

GT.M does not do any memory management related to the output argument - space for the output should be allocated by the external routine. The routine must place the returned environment specification at the address it has allocated and adjust the length accordingly. On a successful return, the return value should be zero. If the translation routine must communicate an error to GT.M, it must return a non-zero value, and if it is to communicate additional error information, place the error text at the address where the environment would normally go and adjust the length to match the length of the error text.

Length of the return value may range from 0-32767, otherwise GT.M reports an error.

A zero-length (empty) string specifies the current value of $ZGBLDIR. Non-zero lengths must represent the actual length of the file specification pointed to by address, excluding any <NUL> terminator. If the address field of the output argument is NULL, GT.M issues an error.

The file specification may be absolute or relative and may contain a logical name. If the file specified is not accessible, or is not a valid global directory, GT.M reports errors in the same way it does for any invalid global directory.

It is possible to write this routine in M (as a call-in), however, global variables in such a routine would change the naked indicator, which environment references normally do not. Depending on the conventions of the application, there might be difficult name-space management issues such as protecting the local variables used by the M routine.

While it is possible for this routine to take any form that the application designer finds appropriate within the given interface definition, the following paragraphs make some recommendations based on the expectation that a routine invoked for any more than a handful of global references should be efficient.

It is expected that the routine loads one or more tables, either at compilation or the first time it is invoked. The logic of the routine performs a look up on the entry in the set of tables. The lookup might be based on the length of the strings and some unique set of characters in the names, or a hash, with collision provisions as appropriate.

The routine may have to deal with a case where one or both of the inputs have zero length. A subset of these cases may have the first string holding a comma limited string that needs to be re-interpreted as being equivalent to two input strings (note that the input strings must never be modified). The routine may also have to handle cases where a value (most likely the first) is accidentally or intentionally, already a global directory specification.

Example:

        >
              type
              gtm_env_translate.c
        #include <stdio.h>
        #include <string.h>
        #include "gtmxc_types.h"
        static int init = 0;
        
        typedef struct
        {
        xc_string_t field1, field2, ret;
        } line_entry ;
        
        static line_entry table[5], *line, linetmp;
        /* Since these errors may occur before setup is complete, they are statics */
        static char *errorstring1 = "Error in function initialization, environment variable GTM_CALLIN_START not defined. Environment translation failed.";
        static char *errorstring2 = "Error in function initialization, function pointers could not be determined. Envrironment translation failed.";
              
        void copy_string(char **loc1, char *loc2, int length)
        {
        char *ptr;
        ptr = (char *) GTM_MALLOC(length);
        strncpy( ptr, loc2, length);
        *loc1 = ptr;
        }
        
        int init_table(xc_string_t *ptr)
        {
        int i = 0;
        char buf[100];
        char *buf1, *buf2;
        FILE *tablefile;
        char *space = " ";
        char *errorstr1 = "Error opening table file table.dat";
        char *errorstr2 = "UNDETERMINED ERROR FROM GTM_ENV_XLATE";
        
        if ((tablefile = fopen("table.dat","r")) == (FILE *)NULL)
        {
         ptr->length = strlen(errorstr1);
         copy_string(&(ptr->address), errorstr1, strlen(errorstr1));
         return 1;
        }
        while (fgets(buf, (int)sizeof(buf), tablefile) != (char *)NULL) 
       {
       line= &table[i++];
       buf1 = buf;
       buf2 =strstr(buf1, space);
       line->field1.length = buf2 - buf1;
       copy_string( &(line->field1.address), buf1, line->field1.length);
       buf1 = buf2+1;
       buf2 = strstr(buf1, space);
       line->field2.length = buf2-buf1;
       copy_string( &(line->field2.address), buf1, line->field2.length);
       buf1 = buf2+1;
       line->ret.length = strlen(buf1) - 1;
       copy_string( &(line->ret.address), buf1, line->ret.length);
       }
       fclose(tablefile);
       /* In this example, the last entry in the table is the error string */
       line = &table[4];
       copy_string( &(line->ret.address), errorstr2, strlen(errorstr2));
       line->ret.length = strlen(errorstr2);
       return 0;
       }
       
       int cmp_string(xc_string_t str1, xc_string_t str2)
       {
       if (str1.length == str2.length)
       return strncmp(str1.address, str2.address, (int) str1.length);
       else
       return str1.length - str2.length;
       }
       
       int cmp_line(line_entry *line1, line_entry *line2)
       {
       return (((cmp_string(line1->field1, line2->field1))||(cmp_string(line1->field2, line2->field2))));
       }
       
       int look_up_table(line_entry *aline, xc_string_t *ret_ptr)
       {
       int i;
       int ret_v;
       
       for(i=0;i<4;i++)
       {
       line = &table[i];
       ret_v = cmp_line( aline, line);
       if (!ret_v)
       {
       ret_ptr->length = line->ret.length;
       ret_ptr->address = line->ret.address;
       return 0;
       }
       }
       /*ERROR OUT*/
       line = &table[4];   
       ret_ptr->length= line->ret.length;
       ret_ptr->address = line->ret.address;
       return 1;
       
       }
       
       int gtm_env_xlate(xc_string_t *ptr1, xc_string_t *ptr2, xc_string_t *ptr_zdir, xc_string_t *ret_ptr)
       {
       
       int return_val, return_val_init;
       if (!init)
       {
       return_val_init = init_functable(ret_ptr);
       if (return_val_init) return return_val_init;
       return_val_init = init_table(ret_ptr); 
       if (return_val_init) return return_val_init;
       init = 1;
       }
       linetmp.field1.length= ptr1->length;
       linetmp.field1.address= ptr1->address;
       linetmp.field2.length= ptr2->length;
       linetmp.field2.address= ptr2->address;
       
       return_val = look_up_table(&linetmp, ret_ptr);
       return return_val;
       }
       > type table.dat
       day1 week1 mumps
       day2 week1 a
       day3 week2 b
       day4 week2 c.gld
            

This example demonstrates the mechanism. A table is set up the first time for proper memory management, and for each reference, a table lookup is performed. Note that for the purpose of simplicity, no error checking is done, so table.dat is assumed to be in the correct format, and have exactly four entries. This routine should be built as a sharedimage, refer to the "Integrating External Routines" chapter for information on building as a shared image.