Someone said to me that the reason that they will not use totally free RPG is they cannot call programs using it. In the earlier flavors of free format RPG they could just switch from free format to fixed to do the call, and return to free format afterwards.
Var1 = 'Hello' ; Var2 = 1 ; Var3 = '' ; C call 'OTHERPGM1' C parm Var1 C parm Var2 C parm Var3 if (Var3 = 'Y') ;
With totally free RPG once the **FREE compiler directive is placed in the code it is not possible to go back to fixed format after that. Therefore, the approach used above is not possible.
For some time there has been alternative method to call programs using, what I call, external program procedure calls. This method even predates free format definitions. But I am just going to discuss how to do this in totally free format.
In the declare procedure prototype definition, DCL-PR, there is a keyword I can use to indicate that this procedure is really an external program. This allows me to call the external program just like any procedure. Let me get straight to the code:
01 **free 02 ctl-opt option(*nodebugio:*srcstmt) ; 03 dcl-pr OtherPgm1 extpgm ; 04 Parm1 char(5) ; 05 Parm2 packed(3) ; 06 Parm3 char(1) ; 07 end-pr ; 08 dcl-s Var1 char(5) ; 09 dcl-s Var2 packed(3) ; 10 dcl-s Var3 char(1) ; 11 Var1 = 'Hello' ; 12 Var2 = 1 ; 13 Var3 = '' ; 14 OtherPgm1(Var1:Var2:Var3) ; 15 if (Var3 = 'Y') ;
Line 1: It is not totally free unless the **FREE in the first position of the source record.
Line 2: My standard control options I use in every program.
Lines 3 – 7: This is the procedure prototype definition for my external program.
Line 3: At the starting line of the procedure prototype definition, DCL-PR, line I give the name I want to call this procedure, and I have to use the EXTPGM keyword to denote that this is an external program. The program is called OTHERPGM1, therefore, my making the procedure name the same as the program's name I do not have to give the program name as part of the EXTPGM keyword. I will show an example of the program and procedure names being different later.
Lines 4 – 6: These are the three parameters that will be passed and returned to and from the external program. In this example I have given them names, which I normally don't do as I cannot use these as variable names later in the program.
Line 7: END-PR marks the end of the procedure prototype definition.
Lines 8 – 10: These are the definitions of the variables I will be using to pass and return from the procedure.
Lines 11 – 13: I am moving values into these variables.
Line 14: I do the call to the external program just as I would for any procedure.
Line 15: The external program can change the values in the parameter variables, that I can then use.
One advantage of using subprocedures is that I can define a procedure within it that remains "local", i.e. can only be used in the subprocedure it is defined it. If I am only going to call an external program from within a subprocedure I can define the procedure prototype with the subprocedure, rather than at the top of the program.
01 dcl-proc Procedure1 ; 02 dcl-pr OtherPgm1Returns extpgm('OTHERPGM1') ; 03 *n char(5) ; 04 *n packed(3) ; 05 *n char(1) ; 06 end-pr ; 07 OtherPgm1Returns(P1:P2:P3) ; 08 end-proc ;
Line 1: DCL-PROC marks the beginning of the subprocedure.
Lines 2 – 6: This is my procedure prototype definition.
Line 2: I am going to call the same program as before, but this time I am giving it a procedure name, OtherPgm1Returns, that is different from the program name. This is why I need to give the external program's name within the EXTPGM keyword.
Lines 3 – 5: This time I am not going to give the parameters names. I need to use *N to indicate that the name is null.
Line 6: END-PR marks the end of the procedure prototype definition.
Line 7: I call the procedure name as I did in the previous example.
Line 8: END-PR marks the end of the procedure.
If the program I am calling does not require parameters to be passed I can define a procedure prototype without any parameters, and call the procedure with no parameters.
01 dcl-pr OtherPgm2 extpgm ; 02 end-pr ; 03 OtherPgm2() ;
Regular readers have seen me do calls to the QCMDEXC API using a procedure.
01 dcl-pr QCMDEXC extpgm ; 02 *n char(250) options(*varsize) const ; 03 *n packed(15:5) const ; 04 end-pr ; 05 dcl-s Command varchar(250) ; 06 dcl-s Length packed(15:5) ; 07 Command = 'CLRPFM QTEMP/WORKFILE' ; 08 QCMDEXC(Command:%len(%trimr(Command))) ; 09 Length = 25 ; 10 QCMDEXC('DLTDTAARA QTEMP/WRKDTAARA':Length) ; 11 QCMDEXC('DLTF QTEMP/WORKFILE':19) ;
In these examples the parameters in the procedure prototype definition have extra keywords on them. OPTIONS(*VARSIZE) mean that the string in the parameter is variable in length. CONST means that the value is passed to the external program, cannot be changed and returned to the calling program/procedure.
Why would I want that? As the examples shown on lines 7 – 11 I don't have to use a variable to pass a value to the external program. I can just pass the value. I tend to do this for two reasons:
- I don't have to define variables that will be used just once, as the parameters, when QCMDEXC is called.
- I think when I give the command I want to execute in the parameter it becomes obvious to myself at a later date, or to another, what I am doing.
It just a case of personal preference, I prefer to do it this way. If you don't then you don't have to.
in the examples I have shown when I have given a value in the EXTPGM it has been a value. But I can use a variable in the EXTPGM keyword, which allows me to call multiple different programs using one procedure prototype definition. Which is the same as using a variable name in the CALL operation code.
01 dcl-pr NextPgm extpgm(PgmName) ; 02 end-pr ; 03 dcl-s PgmName char(10) ; 04 PgmName = 'OTHERPGM2' ; 05 C call PgmName 06 NextPgm() ; 07 PgmName = 'OTHERPGM3' ; 08 NextPgm() ;
line 1: This time the EXTPGMM contains a variable name, rather than a value.
Line 3: This is the variable that will contain the name of the program I want to call.
Line 4: First program I want to call is OTHERPGM2.
Line 5: If I was performing the call using fixed format it would look like this.
Line 6: When I call the procedure it calls the program's name that is in the variable PgmName.
Line 7: Change the name of the program in PgmName.
Line 8: This time when I call the procedure the program OTHERPGM3 is called.
I hope that this post, and the examples contained within, will convince the person I mentioned in the first paragraph, and anyone else who has used the same excuse, that it is possible to call a program in totally free RPG. Now there is no reason why they should not start coding using totally free format.
This article was written for IBM i 7.3, and will work for 7.1 TR11 and 7.2 TR3 and later too.