Usually your program can access variables in the calling Rexx program. You can get, set or delete variables. It is sometimes more complex that the documentation implies.
If your program is called from ISPF you can also set and get ISPF variables, or use ISPF tables to pass data.
To use the Rexx variable interface the applications need access to the ENVironmentBlock (ENVB).
Getting to the ENVB
The Rexx documentation describes how this is passed in register 0, unfortunately a C program does not have access to register 0 (without writing some assembler glue code).
You can get the environment block by calling IRXINIT and passing the parameter “FINDENVB”.
I was unable to use fetch() to dynamically load IRXINIT. (It may work – I couldn’t get it to). Initially I user the binder to include the IRXINIT code, but this is not good practice as you should use the version from the system you are running on.
A better way based on code from David Crayford (thank you) is
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// specify how the irxinit routine is called
#pragma linkage(tsvt_rexxfunc_t,OS)
int main( int argc, char **argv ) {
struct envblock * pEnv;
int rc;
// the TSO anchor block
struct TSVT {
char dummy[140];
char * irxinit;
};
#pragma pack(1)
// this defines a function with parmlist(char *... )
// returning an int
typedef int tsvt_rexxfunc_t( char *, ... );
typedef struct tsvt TSVT;
// TSTV comes from ikjtstv - but no C header equivalent
// so fake one up.
struct tsvt {
int padding[35];
tsvt_rexxfunc_t *irxinit; // Address of IRXEXEC
};
#pragma pack(reset)
int rc2;
// now chain through the control blocks to find the rexx init
// cvtmap provided in SYS1.SIEAHDR.H(CVT)
#define CVTPTR 16L
struct cvtmap * cvt =*( (struct cvtmap ** ) CVTPTR);
// Comment: I think the *((xxx **)) is so unnatural, and always
// get it wrong.
// TSTV comes from ikjtstv - but no C header equivalent
TSVT * tsvt = cvt->cvttvt;
tsvt->irxinit("FINDENVB ",
0, //
0, // instor plist
0, // 4 user field
0, // 5 reserved
&pEnv , // 6 ->envblock
&rc2); // rexx parm 7 return code
printf(" rc2 %i\n",rc2);
}
Set a symbol
This took me a couple of hours to get right. The documentation is not clear in places.
char dummy;
struct shvblock shv;
memset(&shv,0,sizeof(shv));
char * pSymbol = "COLINSSYMBOL";
char * pValue ="VALUECP";
shv. shvcode = 'S'; // SHVSTORE; // symbolic name set
shv. shvnama = pSymbol; // a symbolic name
shv. shvnaml = strlen(pSymbol); // Len symbolic name
shv. shvvala = pValue ;
shv. shvvall = strlen(pValue);
int rc3;
struct irxexte * pExte = (struct irxexte * ) pEnv-> envblock_irxexte;
tsvt_rexxfunc_t * fptr = (tsvt_rexxfunc_t *) pExte -> irxexcom;
rc = (fptr)("IRXEXCOM",
&dummy,
&dummy,
&shv,
&pEnv,
rc3);
rc2 = shv.shvret;
printf("post rc %i rc2 %i rc3 %i\n",rc,rc2,rc3);
Notes:
The Rexx header file provides
#define SHVSTORE "S" /* Set variable from given value */
but you cannot use this because shv. shvcode expects a char ‘S’ , not “S”.
tsvt_rexxfunc_t is used to define the function at address fptr as a z/OS routine with parameter list in register 1, and the high end bit of the last parameter turned on.
After this executed, and the program returned, the Rexx “say COLINSSYMBOL” printed “VALUECP” so it was a success.
A slightly harder case of setting a value.
I put the above code into a subroutine so I was able to use
setSymbol('S',"COLINSSYMBOL","VALUECP");
You can use option upper case ‘S’ which takes the string you give it, and makes a Rexx variable, or you can use the lower case ‘s’ option, which says it does variables substitution.
Uppercase: (The Direct interface). No substitution or case translation takes place. Simple symbols must be valid REXX variable names (that is, in uppercase and not starting with a digit or a period), but in compound symbols any characters (including lowercase, blanks, and so on) are permitted following a valid REXX stem.
This is not entirely true.
With upper case ‘S’
With setSymbol(“MYKEY.aaa“,”VALUECP”), Rexx displayed “MYKEY.AAA” showing the variable did not exist, even though the call to defined it worked successfully.
With setSymbol(“MYKEY.AAA“,”VALUECP”), Rexx displayed “VALUECPA” showing the correct value.
If you are using ‘S’ then always specify the name in upper-case despite what the documentation says.
With lower case ‘s’
Both
setSymbol("MYKEY.aaa","VALUECPA");
setSymbol("mykey.bbb","VALUECPA");
worked.
But it gets more complex…
I had a small Rexx program:
/* REXX */
A = "COLINA"
address link CPLINK "A B C "
drop A
say value("MYKEY.A")
say value("MYKEY.B")
say value("MYKEY.COLINA")
say value("A")
say value("SMYKEY.A")
say value("SMYKEY.B")
say value("SMYKEY.COLINA")
If value(“MYKEY.A”) is “MYKEY.A” then there is no variable with that name.
and my program had
setSymbol('S',"MYKEY.A","BIGSA");
setSymbol('S',"MYKEY.B","BIGSB");
setSymbol('s',"SMYKEY.A","SMALLSA");
setSymbol('s',"SMYKEY.B","SMALLSB");
The output had
- say value(“MYKEY.A”) -> “BIGSA” from my program
- say value(“MYKEY.B”) -> “BIGSB” from my program
- say value(“MYKEY.COLINA”) -> “MYKEY.COLINA” not a variable
- say value(“SMYKEY.A”) -> “SMYKEY.A” not a variable
- say value(“SMYKEY.B”) -> “SMALLSB” set from my program
- say value(“SMYKEY.COLINA”) -> “SMALLSA” ‘.A’ was substituted with COLINA as part of the set call
- Lines 1-3 show that there was no substitution of variables.
- Lines 4 shows that variable SMKEY.A was not created; SMKEY.COLINA was substituted
- Line 5 had no substitution and was like line 2
- Line 6 this is the variable name used.
This means that if you specify a lower case ‘s’, the output may not be as you expect. I would suggest you use upper case ‘S’ unless you know what you are doing.