Seeking Advice on REXX
June 11, 2008 Hey Bruce
I want to call from CL program a REXX program, where the REXX program returns a calculate of date (yymmdd). Do you have an example you could send me?
Thanks in advance,
REXX can’t directly return a value to CL the way other languages do. You’d have to have your REXX procedure write the data to a file or use the REXX External Data Queue.
To use a file you’d have to first override STDOUT to a file of your choice, then use “SAY” to write the data. In your REXX procedure you could do this:
'OVRDBF FILE(STDOUT) TOFILE(yourlib/yourfile) OVRSCOPE(*JOB)' SAY value 'DLTOVR FILE(STDOUT) LVL(*JOB)'
Notice that I put the OVRDBF and DLTOVR in quotes. That way REXX treats them as CL commands. The “value” is whatever value you want returned. The layout of the file can be anything you want as REXX treats all files as flat (it doesn’t understand “externally described”).
To use the REXX External Data Queue you’ll need to push the data to the queue in your REXX procedure, then do an API call in your CL to retrieve the data:
In CL, the length of &DATA and value of &LENGTH can be whatever you need (they just need to match):
DCL VAR(&DATA) + TYPE(*CHAR) + LEN(50) <========= DCL VAR(&LENGTH) + | TYPE(*INT) + | The LEN and VALUE must match LEN(4) + | VALUE(50) <========= DCL VAR(&RETURNCODE) + TYPE(*INT) + LEN(2) CALL PGM(QREXQ) + PARM('P' &DATA &LENGTH 0 &RETURNCODE)
For more information you can check out my article, REXX Can Talk to Other Languages, which describes it in more detail.
I just read your article, but I don’t understand the different when using STRREXPRC, QREXX, or QREXQ. Could you tell me what is the correct form or option that I must to use (1, 2 or 3)
If I write the CL:
PGM DCLF FILE(SOPTEC400/DEP_QHST) DCL VAR(&FH) TYPE(*CHAR) LEN(6) DCL VAR(&LEN) TYPE(*INT) LEN(6) DCL VAR(&RETC) TYPE(*INT) LEN(2) CHGCURLIB CURLIB(SOPTEC400) MONMSG CPF0000 CLRPFM FILE(*LIBL/DEP_QHST) DSPOBJD OBJ(QSYS/QHST*) OBJTYPE(*FILE) + OUTPUT(*OUTFILE) OUTFILE(*LIBL/DEP_QHST) PROCCESS: RCVF MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(FIN)) CHGVAR VAR(&FEC) VALUE(%SST(&ODOBTX 2 6)) 1-> STRREXPRC SRCMBR(FECHA) SRCFILE(SOPTEC400/QREXSRC) PARM(&FH) 2-> CALL PGM(QREXX) PARM(FECHA QREXSRC SOPTEC400 &FH) 3-> CALL PGM(QREXQ) PARM('P' &FH &LEN 0 &RETC)
parse value date('O') with año '/' mes '/' dia d=dia-7; if length(d)<2 then d=0||d FH=año||mes||d return
I’m guessing that the field FH is the value that you wish to return. With that in mind, here is what you need in your REXX. Before your “return” add the following line:
push FH <=== This places the value in field FH into the External Data Queue
In your CL you should have the following:
DCL VAR(&FH) TYPE(*CHAR) LEN(6) DCL VAR(&LEN) TYPE(*INT) LEN(4) VALUE(6) DCL VAR(&RETC) TYPE(*INT) LEN(2) STRREXPRC SRCMBR(FECHA) SRCFILE(SOPTEC400/QREXSRC) <===
This executes your REXX:
CALL PGM(QREXQ) PARM('P' &FH &LEN 0 &RETC) <===
This retrieves the value that was “pushed” to the External Data Queue.
Notice that I removed the PARM from the STRREXPRC statement. PARMs can only be passed into REXX, not returned. If I understand your REXX procedure properly you are not passing a value in, simply calculating a date value and returning it. Also, I corrected your definition of field &LEN. It needs to be TYPE(*INT) LEN(4) with a VALUE indicating the length of the data being returned (which should match the LEN of field &FH). It is the VALUE that must match the LEN of &FH. This VALUE is passed to QREXQ and tells the program how much data to return.
One more thing, program QREXX is just another way to execute a REXX procedure (like STRREXPRC command does). So, you could replace your STRREXPRC command with a call to QREXX, but since you’re using CL, there is no good reason to do that. If you were calling your REXX from RPG or COBOL, then it might make more sense.
Let me know if you have more questions.
It’s me again. Your advice worked. I prefer to work in REXX because I have worked 18 years with mainframe (VM and VSE), and my company just changed machines from zSeries for iSeries three years ago. But my co-workers do not know REXX, only CL. Is there some trick for doing the following in CL?
l.1='LINEATM';l.2='LINEHSM';l.3='LINELOCAL';l.4='LINEVISA';l.5='LINEMC' do k=1 to 5 "VFYCFG CFGOBJ("l.k") CFGTYPE(*LIN) STATUS(*ON)" end exit
There is a way to do this in CL, but it is a bit more cumbersome than the REXX option. Also, it depends on what version of the OS you’re at.
CL doesn’t support arrays, but you can do something like it. If you’re at V5R4 you can use pointers in CL, otherwise you’d need to use %SST.
PGM DCL VAR(&LINES) TYPE(*CHAR) LEN(50) VALUE('LINEATM LINEHSM LINELOCAL LINEVISA LINEMC ') <== Each value is 10 long, total 50 chars. DCL VAR(&LINEPTR) TYPE(*PTR) ADDRESS(&LINES) DCL VAR(&CURLINE) TYPE(*CHAR) STG(*BASED) LEN(10) BASPTR)&LINEPTR) DCL VAR(&I) TYPE(*UINT) DOFOR VAR(&I) FROM(1) TO(5) CHGVAR VAR(&LINEPTR) VALUE(&LINEPTR + ((&I - 1) * 10)) VRYCFG CFGOBJ(&CURLINE) CFGTYPE(*LIN) STATUS(*ON) ENDDO ENDPGM
PGM DCL VAR(&LINES) TYPE(*CHAR) LEN(50) VALUE('LINEATM LINEHSM LINELOCAL LINEVISA LINEMC ') <== Each value is 10 long, total 50 chars. DCL VAR(&LINEPOS) TYPE(*DEC) LEN(3 0) VALUE(0) DCL VAR(&CURLINE) TYPE(*CHAR) STG(*BASED) LEN(10) BASPTR)&LINEPTR) DCL VAR(&I) TYPE(*UINT) DOFOR VAR(&I) FROM(1) TO(5) CHGVAR VAR(&LINEPOS) VALUE(((&I - 1) * 10) + 1) CHGVAR VAR(&CURLINE) VALUE(%SST(&LINES &LINEPOS 10)) VRYCFG CFGOBJ(&CURLINE) CFGTYPE(*LIN) STATUS(*ON) ENDDO ENDPGM
If you’re at a release before DOFOR came out, you’ll have to modify your logic to increment &I from 1 to 5, otherwise the second example stays the same. (I’ll leave that to your co-workers.)
I haven’t tested the above code, but it should be pretty close.