3.3 Interlanguage Calling

In DEC C, you can call external routines written in other languages or DEC C routines from routines written in other languages as either functions or subroutines. When you call an external routine as a function, a single value is returned. When you call an external routine as a subroutine (a void function), any values are returned in the argument list.

By default, DEC C passes all arguments by immediate value with the exception of arrays and functions; these are passed by reference. Table 3-9 lists the default passing mechanisms for other OpenVMS languages.

Table 3-9 Default Passing Mechanisms

Language  Arrays  Numeric Data  Character Data 
MACRO  No default  No default  No default 
Pascal  Reference  Reference  Descriptor 
BASIC  Descriptor  Reference  Descriptor 
COBOL  N/A  Reference  Reference 
FORTRAN  Reference  Reference  Descriptor 

The following sections describe the methods involved in using DEC C with routines written in other OpenVMS languages.

3.3.1 Calling VAX FORTRAN

When calling VAX FORTRAN from DEC C or vice versa, note these considerations. VAX FORTRAN argument lists and argument descriptors are usually allocated statically. When it is possible, and to optimize space and time, the VAX FORTRAN compiler pools the argument lists and initializes them at compile time. Sometimes several calls may use the same argument list.

In DEC C, you often use arguments as local variables, and modify them at will. If a DEC C routine that modifies an argument is called from a VAX FORTRAN routine, unintended and incorrect side effects may occur.

The following example shows a DEC C routine that is invalid when called from VAX FORTRAN:

void f(int *x)      /* x is a FORTRAN INTEGER passed by reference */

{
    /* The next assignment is OK.  It is permitted to modify what a
     * FORTRAN argument list entry points to. */
    *x = 0;                /* ok */

    /* The next assignment is invalid.  It is not permitted to modify
     * a FORTRAN argument list entry itself. */
    x = x + 1;                /* Invalid */
}

Another problem is the semantic mismatch between strings in C and strings in VAX FORTRAN. Strings in C vary in length and end in a null character. Strings in VAX FORTRAN do not end in a null character and are padded with spaces to some fixed length. In general, this mismatch means that strings may not be passed between DEC C and VAX FORTRAN unless you do additional work. You may make a VAX FORTRAN routine add a null character to a CHARACTER string before calling a DEC C function. You may also write code that explicitly gets the length of a VAX FORTRAN string from its descriptor and carefully pads the string with spaces after modifying it. An example later in this section shows a C function that carefully produces a proper string for VAX FORTRAN.

Example 3-5 shows a DEC C function calling a VAX FORTRAN subprogram with a variety of data types. For most scalar types, VAX FORTRAN expects arguments to be passed by reference but character data is passed by descriptor.

Example 3-5 DEC C Function Calling a VAX FORTRAN Subprogram

/*
 *  Beginning of DEC C function:
 */

#include <stdio.h>
#include <descrip.h>                 /* Get layout of descriptors */

extern int fort();                   /* Declare FORTRAN function */

main(void)
{
    int i = 508;
    float f = 649.0;
    double d = 91.50;
    struct {
        short s;
        float f;
    } s = {-2, -3.14};
    auto $DESCRIPTOR(string1, "Hello, FORTRAN");
    struct dsc$descriptor_s string2;


    /* "string1" is a FORTRAN-style string declared and initialized using the
     * $DESCRIPTOR macro.  "string2" is also a FORTRAN-style string, but we are
     * declaring and initializing it by hand. */
    string2.dsc$b_dtype = DSC$K_DTYPE_T;  /* Type is CHARACTER */
    string2.dsc$b_class = DSC$K_CLASS_S;  /* String descriptor */
    string2.dsc$w_length = 3;             /* Three characters in string */
    string2.dsc$a_pointer = "bye";        /* Pointer to string value */

    printf("FORTRAN result is %d\n", fort(&i, &f, &d, &s, &string1, &string2));
}   /* End of DEC C function */


C
C     Beginning of VAX FORTRAN subprogram:
C
      INTEGER FUNCTION FORT(I, F, D, S, STRING1, STRING2)
      INTEGER I
      REAL F
      DOUBLE PRECISION D
      STRUCTURE /STRUCT/
      INTEGER*2 SHORT
      REAL FLOAT
      END STRUCTURE
      RECORD /STRUCT/ S

C     You can tell FORTRAN to use the length in the descriptor
C     as done here for STRING1, or you can tell FORTRAN to ignore the
C     descriptor and assume the string has a particular length as done
C     for STRING2.  This choice is up to you.
      CHARACTER*(*) STRING1
      CHARACTER*3 STRING2


      WRITE(5, 10) I, F, D, S.SHORT, S.FLOAT, STRING1, STRING2
10    FORMAT(1X, I3, F8.1, D10.2, I7, F10.3, 1X, A, 2X, A)
      FORT = -15
      RETURN
      END
C     End of VAX FORTRAN subprogram

Example 3-5 produces the following output:

508   649.0  0.92D+02     -2    -3.140 Hello, FORTRAN  bye
FORTRAN result is -15

Example 3-6 shows a VAX FORTRAN subprogram calling a DEC C function. Since the DEC C function is called from VAX FORTRAN as a subroutine and not as a function, the DEC C function is declared to have a return value of void.

Example 3-6 VAX FORTRAN Subprogram Calling a DEC C Function

C
C     Beginning of VAX FORTRAN subprogram:
C
      INTEGER I
      REAL F(3)
      CHARACTER*10 STRING

C     Since this program does not have a C main program and you want
C     to use DEC C RTL functions from the C subroutine, you must call
C     VAXC$CRTL_INIT to initialize the run-time library.
      CALL VAXC$CRTL_INIT


      I = -617
      F(1) = 3.1
      F(2) = 0.04
      F(3) = 0.0016
      STRING = 'HELLO'

      CALL CSUBR(I, F, STRING)
      END
C     End of VAX FORTRAN subprogram


/*
 *  Beginning of DEC C function:
 */
#include <stdio.h>
#include <descrip.h>                       /* Get layout of descriptors */

void csubr(int *i,                         /* FORTRAN integer, by reference */
    float f[3],                            /* FORTRAN array, by reference */
    struct dsc$descriptor_s *string)       /* FORTRAN character, by descriptor */
{
    int j;

    printf("i = %d\n", *i);

    for (j = 0; j < 3; ++j)
        printf("f[%d] = %f\n", j, f[j]);


    /* Since FORTRAN character data is not null-terminated, you must use
     * a counted loop to print the string.
     */
    printf("string = \"");
    for (j = 0; j < string->dsc$w_length; ++j)
        putchar(string->dsc$a_pointer[j]);
    printf("\"\n");

}   /*  End of DEC C function  */

Example 3-6 produces the following output:

i = -617
f[0] = 3.100000
f[1] = 0.040000
f[2] = 0.001600
string = "HELLO     "

Example 3-7 shows a C function that acts like a CHARACTER*(*) function in VAX FORTRAN.

Example 3-7 DEC C Function Emulating a VAX FORTRAN CHARACTER*(*) Function

C
C       Beginning of VAX FORTRAN program:
C
        CHARACTER*9 STARS, C

C       Call a C function to produce a string of three "*" left-justified
C       in a nine-character field.
        C = STARS(3)

        WRITE(5, 10) C
10      FORMAT(1X, '"', A, '"')
        END
C       End of VAX FORTRAN program


/*
 *  Beginning of DEC C function:
 */

#include <descrip.h>                        /* Get layout of descriptors */

/* Routine "stars" is equivalent to a FORTRAN function declared as
 * follows:
 *
 *        CHARACTER*(*) FUNCTION STARS(NUM)
 *        INTEGER NUM
 *
 * Note that a FORTRAN CHARACTER function has an extra entry added to
 * the argument list to represent the return value of the CHARACTER
 * function.  This entry, which appears first in the argument list,
 * is the address of a completely filled-in character descriptor.  Since
 * the C version of a FORTRAN character function explicitly uses this
 * extra argument list entry, the C version of the function is void!
 *
 * This example function returns a string that contains the specified
 * number of asterisks (or "stars").
 *
 */


void stars(struct dsc$descriptor_s *return_value, /* FORTRAN return value */
           int *num_stars)                 /* Number of "stars" to create */
{
    int i, limit;


    /* A FORTRAN string is truncated if it is too large for the memory area
     * allocated, and it is padded with spaces if it is too short.  Set limit
     * to the number of stars to put in the string given the size of the area
     * used to store it. */
    if (*num_stars < return_value->dsc$w_length)
        limit = *num_stars;
    else
        limit = return_value->dsc$w_length;

    /* Create a string of stars of the specified length up to the limit of the
     * string size. */
    for (i = 0; i < limit; ++i)
        return_value->dsc$a_pointer[i] = '*';

    /* Pad rest of string with spaces, if necessary. */
    for (; i < return_value->dsc$w_length; ++i)
        return_value->dsc$a_pointer[i] = ' ';
}  /*  End of DEC C Function   */

Example 3-7 produces the following output:

"***      "

3.3.2 Calling VAX MACRO

You can call a VAX MACRO routine from DEC C or vice versa. However, like all interlanguage calls, it is necessary to make sure that the actual arguments correspond to the expected formal parameter types. Also, it is necessary to remember that C strings are null-terminated and to take special action in either the MACRO routine or the C routine to allow for this.

Example 3-8 shows a MACRO routine that calls a C routine with three arguments, passing one by value, one by reference, and one by descriptor. It is followed by the source for the called C routine.

Example 3-8 VAX MACRO Program Calling a DEC C Function

;-----------------------------------------------------------------------
; Beginning of MACRO program
;-----------------------------------------------------------------------
        .extrn  dbroutine        ; The C routine
;-----------------------------------------------------------------------
; Local Data
;-----------------------------------------------------------------------

        .psect        data        rd,nowrt,noexe

        ft$$t_part_num:        .ascii /WidgitGadget/
        ft$$t_query_mode:        .ascii /I/
        ft$$s_query_mode =        <. - ft$$t_query_mode>
        ft$$l_protocol_buff:        .blkl        1
        ft$$kd_part_num_dsc:
                        .word   12
                        .word        0
                        .address ft$$t_part_num


;-----------------------------------------------------------------------
; Entry Point
;-----------------------------------------------------------------------
        .psect        ft_code        rd,nowrt,exe
        .entry dbtest        ^m<r2,r3,r4,r5,r6,r7,r8>

;+
; call C routine for data base lookup
;-
        movl        #1,r3
        pushal      ft$$kd_part_num_dsc             ; Descriptor for part number
        pushal      ft$$t_query_mode                ; Mode to call
        pushl       #1                              ; Status
        calls       #3, dbroutine                   ; Check the data base
99$:
        ret

        .end dbtest
;-----------------------------------------------------------------------
; End of MACRO program
;-----------------------------------------------------------------------


/*
 *  Beginning of DEC C code for dbroutine:
 */

#include <stdio.h>
#include <descrip.h>

/* Structure pn_desc is the format of the descriptor
   passed by the macro routine. */
extern     struct
       mydescript    {
               short  pn_len;
               short  pn_zero;
               char   *pn_addr;
               };


int  dbroutine (int status,                    /* Passed by value      */
                char *action,                  /* Passed by reference  */
                struct mydescript *name_dsc)   /* Passed by descriptor */
{
        char *part_name;

        /* Allocate space to put the null-padded name string. */
        part_name = malloc(name_dsc->pn_len + 1);
        memcpy( part_name,name_dsc -> pn_addr ,name_dsc -> pn_len);

        /* Remember that C array bounds start at 0 */
        part_name[name_dsc -> pn_len] = '\0';


        printf (" Status is %d\n", status);
        printf (" Length  is %d\n",name_dsc -> pn_len);
        printf (" Part_name is %s\n",part_name);
        printf (" Request is %c\n",*action);
        status = 1;
        return(status);
}  /*  End of DEC C code for dbroutine  */

Example 3-8 produces the following output:

 Status is 1
 Length  is 12
 Part_name is WidgitGadget
 Request is I

Example 3-9 shows a DEC C program that calls a VAX MACRO program.

Example 3-9 DEC C Program Calling a VAX MACRO Program

/* Beginning of DEC C function  */

#include <stdio.h>
#include <descrip.h>

int zapit( int status, int *action, struct dsc$descriptor_s *descript);


main(void)
{
    int status=255, argh = 99;
    int *action = &argh;
    $DESCRIPTOR(name_dsc,"SuperEconomySize");

    printf(" Before calling ZAPIT: \n");
    printf(" Status was %d \n",status);
    printf(" Action contained %d and *action contained %d \n" ,action, *action);
    printf(" And the thing described by the descriptor was %s\n",
            name_dsc.dsc$a_pointer);


    if (zapit(status,action,&name_dsc) && 1)
    {
      printf(" Ack, the world has been zapped! \n");
      printf(" Status is %d \n",status);
      printf(" Action contains %d and *action contains %d \n" ,action, *action);
      printf(" And the address of the thing described by the descriptor is %d\n",
              name_dsc.dsc$a_pointer);

    }
}  /*  End of DEC C function


;-----------------------------------------------------------------------
;  Beginning of VAX MACRO source code for zapit
;-----------------------------------------------------------------------
; Entry Point
;-----------------------------------------------------------------------
        .psect        ft_code        rd,nowrt,exe
        .entry zapit        ^m<r2,r3,r4,r5,r6,r7,r8>

;+
; Maliciously change parameters passed by the C routine.
;
; The first parameter is passed by value, the second by
; reference, and the third by descriptor.
;-

        movl        4(ap), @8(ap)        ;Change the by-reference parameter
                                         ;to the first parameter's value.

        movl        12(ap), r2
        movl        #0,4(r2)             ;Zap address of string in descriptor.

        ; Return -1 to signal successful destruction.
        movl        #-1,r0
        ret

        .end
;-----------------------------------------------------------------------
;  End of VAX MACRO source code for zapit
;-----------------------------------------------------------------------

Example 3-9 produces the following output:

 Before calling ZAPIT:
 Status was 255
 Action contained 2146269556 and *action contained 99
 And the thing described by the descriptor was SuperEconomySize
 Ack, the world has been zapped!
 Status is 255
 Action contains 2146269556 and *action contains 255
 And the address of the thing described by the descriptor is 0

3.3.3 Calling VAX BASIC

Calling routines written in VAX BASIC from DEC C programs or vice versa is straightforward. By default, VAX BASIC passes arguments by reference, except for arrays and strings, which are passed by descriptor. In some cases, these defaults may be overridden by explicitly specifying the desired parameter-passing mechanisms in the VAX BASIC program. However, if an argument is a constant or an expression, the actual argument passed refers to a local copy of the specified argument's value.

Strings in VAX BASIC are not terminated by a null character, which is done by DEC C. As a result, passing strings between VAX BASIC and DEC C routines requires you to do additional work. You may choose to add an explicit null character to a VAX BASIC string before passing it to a DEC C routine, or you may prefer to code the DEC C routine to obtain the string's length from its descriptor.

Example 3-10 shows a DEC C program that calls a VAX BASIC function with a variety of argument data types.

Example 3-10 DEC C Function Calling a VAX BASIC Function

/*
 *  Beginning of DEC C function:
 */

#include <stdio.h>
#include <descrip.h>

        extern        int        basfunc ();

        main(void)
        {
                int        i = 508;
                float        f = 649.0;
                double        d = 91.50;
                struct
                {
                        short        s;
                        float        f;
                }        s = { -2, -3.14 };
                $DESCRIPTOR (string1, "A C string");

                printf ("BASIC returned %d\n",
                        basfunc (&i, &f, &d, &s, &string1, "bye"));
        }  /*  End of DEC C function   */


        REM  Beginning of the VAX BASIC program
        FUNCTION INTEGER basfunc (INTEGER i, REAL f, DOUBLE d, x s, &
                                  STRING string1,                    &
                                  STRING string2 = 3 BY REF)

            RECORD        x
                WORD    s
                REAL    f
            END RECORD x


            PRINT 'i = '; i
            PRINT 'f = '; f
            PRINT 'd = '; d
            PRINT 's::s = '; s::s
            PRINT 's::f = '; s::f
            PRINT 'string1 = '; string1
            PRINT 'string2 = '; string2

        END FUNCTION -15
        REM  End of the VAX BASIC program

Example 3-10 produces the following output:

        i =  508
        f =  649
        d =  91.5
        s::s = -2
        s::f = -3.14
        string1 = A C string
        string2 = bye
        BASIC returned -15

Example 3-11 shows a VAX BASIC program that calls a DEC C function.

Example 3-11 VAX BASIC Program Calling a DEC C Function

        REM  Beginning of the VAX BASIC program:
        PROGRAM example

            EXTERNAL STRING FUNCTION cfunc (INTEGER BY VALUE, &
                                            INTEGER BY VALUE, &
                                            STRING BY DESC)

            s$ = cfunc (5, 3, "abcdefghi")
            PRINT "substring is "; s$

        END PROGRAM
        REM  End of the VAX BASIC program


/*
 *  Beginning of DEC C function:
 */

#include <descrip.h>

        /*
         *  This routine simulates a VAX BASIC function whose return
         *  value is a STRING.  It returns the substring that is `length'
         *  characters long, starting from the offset `offset' (0-based)
         *  in the input string described by the descriptor pointed to
         *  by `in_str'.
         */

        void        cfunc (struct dsc$descriptor_s *out_str,
                       int offset,
                       int length,
                       struct dsc$descriptor_s *in_str)
        {
                /* Declare a string descriptor for the substring. */
                struct        dsc$descriptor        temp;

                /* Check that the desired substring is wholly
                   within the input string. */
                if (offset + length > in_str -> dsc$w_length)
                        return;


                /* Fill in the substring descriptor. */
                temp.dsc$w_length = length;
                temp.dsc$a_pointer = in_str -> dsc$a_pointer + offset;
                temp.dsc$b_dtype = DSC$K_DTYPE_T;
                temp.dsc$b_class = DSC$K_CLASS_S;

                /* Copy the substring to the return string. */
                str$copy_dx (out_str, & temp);
        }  /*  End of DEC C function  */

Example 3-11 produces the following output:

        substring is fgh

3.3.4 Calling VAX Pascal

Like VAX FORTRAN and VAX BASIC, there are certain considerations that you must take into account when calling VAX Pascal from DEC C and vice versa. When calling VAX Pascal from DEC C, VAX Pascal expects all parameters to be passed by reference. In VAX Pascal, there are two different types of semantics: value and variable. The value semantics in VAX Pascal are different from passing by value in DEC C. Because they are different, you must specify the address of the C parameter.

VAX Pascal also expects all strings to be passed by descriptor. If you use the CLASS_S descriptor, the string is passed by using VAX Pascal semantics. If the content of the string is changed, it is not reflected back to the caller.

Example 3-12 is an example of how to call a VAX Pascal routine from DEC C.

Example 3-12 DEC C Function Calling a VAX Pascal Routine

/*
 * Beginning of DEC C function:
 */

#include <descrip.h>

/* This program demonstrates how to call a Pascal routine
   from a C function. */

/* A Pascal routine called by a C function. */
extern        void        Pascal_Routine ();

main()
{
    struct dsc$descriptor_s to_Pascal_by_desc;
    char *Message = "The_Max_Num";
    int to_Pascal_by_value = 100,
    to_Pascal_by_ref = 50;


    /* Construct the descriptor. */
    to_Pascal_by_desc.dsc$a_pointer = Message;
    to_Pascal_by_desc.dsc$w_length = strlen (Message);
    to_Pascal_by_desc.dsc$b_class = DSC$K_CLASS_S;
    to_Pascal_by_desc.dsc$b_dtype = DSC$K_DTYPE_T;

    /* Pascal expects a calling routine to pass parameters by reference. */


    Pascal_Routine(&to_Pascal_by_value, &to_Pascal_by_ref, &to_Pascal_by_desc);

    printf ("\nWhen returned from Pascal:\nto_Pascal_by_value is still \
%d\nBut to_Pascal_by_ref is %d\nand Message is still %s\n",
            to_Pascal_by_value, to_Pascal_by_ref,
            to_Pascal_by_desc.dsc$a_pointer);
}  /*  End of DEC C function   */

{
  Beginning of VAX Pascal routine
}

MODULE C_PASCAL(OUTPUT);

 { This Pascal routine calls the Pascal MAX function
   to determine the maximum value between
   'from_c_by_value` and 'from_c_by_ref`, and then
   assigns the result back to 'from_c_by_ref`.
   It also tries to demonstrate the results of passing
   a by-descriptor mechanism.
   It is called from a C main function.
 }
[GLOBAL]PROCEDURE Pascal_Routine
      (          from_c_by_value :INTEGER;
      VAR from_c_by_ref :INTEGER;
          from_c_by_desc :[ CLASS_S ] PACKED ARRAY [l1..u1:INTEGER] OF CHAR
      );

    VAR
        today_is : PACKED ARRAY [1..11] OF CHAR;

    BEGIN


    { Display the contents of formal parameters. }
    WRITELN;
    WRITELN ('Parameters passed from C function: ');
    WRITELN ('from_c_by_value: ', from_c_by_value:4);
    WRITELN ('from_c_by_ref: ', from_c_by_ref:4);
    WRITELN ('from_c_by_desc: ', from_c_by_desc);

    { Assign the maximum value into 'from_c_by_ref` }
    from_c_by_ref := MAX (from_c_by_value, from_c_by_ref);

    { Change the content of 'from_Pascal_by_value` --
      to show that the value did not get
      reflected back to the caller.
     }
    from_c_by_value := 20;


    { Put the results of DATE into 'from_c_by_desc`
      to show that the CLASS_S is only valid with
      comformant strings passed by value.
     }
    DATE (today_is);
    from_c_by_desc := today_is;
    WRITELN ('***********************');
    WRITELN ('from_c_by_desc is changed to today''s date: "',
                      from_c_by_desc, '"');
    WRITELN ('BUT, this will not reflect back to the caller.');
END;
END.
{
  End of VAX Pascal routine
}

Example 3-12 produces the following output:

from_c_by_value:  100
from_c_by_ref:   50
from_c_by_desc: The_Max_Num
***********************
from_c_by_desc is changed to today's date "26-MAY-1992"
BUT, this will not reflect back to the caller.

When returned from Pascal:
to_Pascal_by_value is still 100
to_Pascal_by_ref is  100
and Message is still The_Max_Num

There are also some considerations when calling DEC C from VAX Pascal. For example, you can use mechanism specifiers such as %IMMED, %REF, and %STDESCR in VAX Pascal. When you use the %IMMED mechanism specifier, the compiler passes a copy of a value rather than an address. When you use the %REF mechanism specifier, the address of the actual parameter is passed to the called routine, which is then allowed to change the value of the corresponding actual parameter. When you use the %STDESCR mechanism specifier, the compiler generates a fixed-length descriptor of a character- string variable and passes its address to the called routine. For more information on these mechanism specifiers and others, see the VAX Pascal documentation.

Another consideration is that VAX Pascal does not null-pad strings. Therefore, you must add a null character to make the string a C string. Also, when passing a string from VAX Pascal to DEC C, you can declare a structure declaration in DEC C that corresponds to the VAX Pascal VARYING TYPE declaration.

Example 3-13 shows an example of how to call DEC C from VAX Pascal.

Example 3-13 VAX Pascal Program Calling a DEC C Function

{
  Beginning of VAX Pascal function:
}

PROGRAM PASCAL_C (OUTPUT);

CONST
        STRING_LENGTH = 80;

TYPE
        STRING = VARYING [STRING_LENGTH] OF CHAR;

VAR
    by_value : INTEGER;
    by_ref : STRING;
    by_desc: PACKED ARRAY [1..10] OF CHAR;

[EXTERNAL]
PROCEDURE VAXC$CRTL_INIT; EXTERN;

[EXTERNAL]
PROCEDURE c_function
 (   %immed      by_value :  INTEGER;
     %ref        by_ref :    STRING ;
     %stdescr    by_desc:    PACKED ARRAY [l1..u1:INTEGER] OF CHAR
 );  EXTERN;


BEGIN

    { Establish the appropriate DEC C RTL environment for
       calling the DEC C RTL from Pascal.
    }
    VAXC$CRTL_INIT;

    by_value := 1;

    {                NOTE
       Pascal does not null pad a string.
       Therefore, the LENGTH built-in function counts
       the null pad character while the DEC C library function strlen
       does not include the terminating null character.
     }

    by_ref := 'TO_C_BY_REF'(0)'';
    by_desc := 'TERM'(0)'';


    { Call a C function by passing parameters
       using foreign semantics.
    }
    c_function (by_value, by_ref, by_desc);

    WRITELN;
    WRITELN;
    WRITELN ('*************************');
    WRITELN ('After calling C_FUNCTION: ');
    WRITELN;
    WRITELN ('by_value is still ',by_value:3);
    WRITELN ('however, by_ref contains ',by_ref,
                     ' (aka Your Terminal Type)');
    WRITELN ('and, by_desc still contains ',by_desc);

END.
{
  End of VAX Pascal program
}

/*
 *  Beginning of DEC C function:
 *
 *
 * A C function called from the Pascal routine.
 * The parameters are passed to a C function
 * by value, by reference, and by descriptor,
 * respectively.
 */
#include <descrip.h>

/* A Pascal style of VARYING data type. */
struct Pascal_VARYING
{
    unsigned short        length;
    char                string[80];
};

/* This C function calls the DEC C RTL function getenv() and puts
 * your terminal type in 'from_Pascal_by_ref`.
 * It is called from a Pascal program.
 */
void        c_function  (unsigned char            from_Pascal_by_value,
                    struct Pascal_VARYING   *from_Pascal_by_ref,
                    struct dsc$descriptor_s *from_Pascal_by_desc
                    )
{
    char *term;

    /* Display the contents of formal parameters. */
    printf ("\nParameters passed from Pascal:\n");
    printf ("from_Pascal_by_value: %d\nfrom_Pascal_by_ref: %s\n\
from_Pascal_by_desc: %s\n", from_Pascal_by_value,
                              from_Pascal_by_ref -> string,
                              from_Pascal_by_desc -> dsc$a_pointer);

    if ((term = getenv(from_Pascal_by_desc -> dsc$a_pointer)) != 0)
    {
          /* Fill 'from_Pascal_by_ref` with new value. */
             strcpy (from_Pascal_by_ref -> string, term);
             from_Pascal_by_ref -> length = strlen (term);

          /* Change the contents of 'from_Pascal_by_value` --
           * to demonstrate that the value did not get
           * reflected back to the calling routine.
           */
             from_Pascal_by_value = from_Pascal_by_desc -> dsc$w_length
                               + from_Pascal_by_ref -> length;
    }

    else
             printf ("\ngetenv\(\"TERM\"\) is undefined.");

}   /*  End of DEC C function   */

Example 3-13 produces the following output:

Parameters passed from Pascal:
from_Pascal_by_value: 1
from_Pascal_by_ref: TO_C_BY_REF
from_Pascal_by_desc: TERM

*************************
After calling C_FUNCTION:

by_value is still 1
however, by_ref contains vt200-80 (aka Your Terminal Type)
and, by_desc still contains TERM


Previous Page | Next Page | Table of Contents | Index