Wednesday, July 17, 2019

Easiest way to retrieve the program's name in a CL program

this is the easiest way to get the cl program name

It has always been a bit of a pain to get the name of the CL current program using the Receive Message command, RCVMSG. Therefore, I was excited when I received an example program showing a much simpler way of getting the same information by using a Machine Interface program.

The program I am going to show needs to be created as a CLLE member, and compiled using the Create Bound CL program , CRTBNDCL. As the call procedure command, CALLPRC, is not permitted in CLP source members.

My CLLE program will be calling the MI program _MATPGMNM, it is important to notice that the program name starts with an underscore ( _ ). I cannot use a CALL command, I must use the CALLPRC command instead.

My first example is the simplest program I could write to call this MI program, and have the current program name and library returned.

01  PGM

02  DCL VAR(&DATA) TYPE(*CHAR) LEN(80)
03  DCL VAR(&PGM) TYPE(*CHAR) LEN(10)
04  DCL VAR(&PGMLIB) TYPE(*CHAR) LEN(10)

05  CHGVAR VAR(%BIN(&DATA 1 4)) VALUE(80)
06  CHGVAR VAR(%BIN(&DATA 5 4)) VALUE(80)
07  CHGVAR VAR(%BIN(&DATA 9 4)) VALUE(0)
08  CHGVAR VAR(%BIN(&DATA 13 4)) VALUE(0)

09  CALLPRC PRC('_MATPGMNM') PARM((&DATA))

10  CHGVAR VAR(&PGM) VALUE(%SST(&DATA 51 10))
11  CHGVAR VAR(&PGMLIB) VALUE(%SST(&DATA 19 10))

12  ENDPGM

Line 2: This is the parameter that will be passed, and returned, from the MI program.

Lines 3 and 4: These are the variables I will be putting the program and library name into at the end of the program.

Lines 5 – 8: I need to load certain values into &DATA. The first value of 80, starting in position 1, is the length of the variable I am passing to the MI program. The second 80 is, starting in position 5, is the length of variable I want returned. As far as I can find the other numbers, zeroes in positions 9 and 13, are needed just to make _MATPGMNM work correctly.

Line 9: In this call procedure command the procedure name is a string, therefore, it must be enclosed within apostrophes ( ' ).

Lines 10 and 11: I am extracting the program and library name from &DATA.

If I run debug and have my breakpoint at line 12 I can see the values in &PGM and &PGMLIB:

> EVAL &pgm
  &PGM = 'TESTCLP   '
> EVAL &pgmlib
  &PGMLIB = 'MYLIB     '

But I am not going to want to add all of this code to every CL program I want to retrieve the program name for. I could make this a CL procedure and bind it to other CL programs, but I have shown how to do that before. In this example I am going to put this into its own source member, and then use the equivalent of RPG's /COPY to include it into the source of another CL program.

The external member is going to contain a subroutine that does the call to the MI program:

01  SUBR SUBR(CPY0031)                              
02    CHGVAR VAR(%BIN(&DATA80 1 4)) VALUE(80)
03    CHGVAR VAR(%BIN(&DATA80 5 4)) VALUE(80)
04    CHGVAR VAR(%BIN(&DATA80 9 4)) VALUE(0)
05    CHGVAR VAR(%BIN(&DATA80 13 4)) VALUE(0)

06    CALLPRC PRC('_MATPGMNM') PARM((&DATA80))

07    CHGVAR VAR(&PGM) VALUE(%SST(&DATA80 51 10))
08    CHGVAR VAR(&PGMLIB) VALUE(%SST(&DATA80 19 10))
09  ENDSUBR

Line 1: I call these "external" subroutines the same name as the source member they are in. I think it makes it easier to find which member contains which subroutine.

I cannot define variables within the subroutine. They have to be defined at the beginning of the program that calls this subroutine.

The program that has the subroutine's source member copied into it looks like:

01  PGM

02  DCL VAR(&DATA80) TYPE(*CHAR) LEN(80)
03  DCL VAR(&PGM) TYPE(*CHAR) LEN(10)
04  DCL VAR(&PGMLIB) TYPE(*CHAR) LEN(10)

05  CALLSUBR SUBR(CPY0031)

06  INCLUDE SRCMBR(CPY0031) SRCFILE(MYLIB/CPYSRC)

07  ENDPGM

Lines 2 – 4: These are the variables that will be used by this program, and the external subroutine.

Line 5: This is the line to call the subroutine.

Line 6: The include command, INCLUDE, is what is used to copy the source code from one CL member into another. The two parameters, in my opinion, are obvious what they do.

When I look at the compile listing I can see where the subroutine has been included into the source of the main program.

900- /* START INCLUDE SRCMBR(CPY0031) SRCFILE(MYLIB/CPYSRC) */
1000- SUBR SUBR(CPY0031)
1100-   CHGVAR VAR(%BIN(&DATA80 1 4)) VALUE(80)
1200-   CHGVAR VAR(%BIN(&DATA80 5 4)) VALUE(80)
1300-   CHGVAR VAR(%BIN(&DATA80 9 4)) VALUE(0)
1400-   CHGVAR VAR(%BIN(&DATA80 13 4)) VALUE(0)
1500-
1600-   CALLPRC PRC('_MATPGMNM') PARM((&DATA80))
1700-
1800-   CHGVAR VAR(&PGM) VALUE(%SST(&DATA80 51 10))
1900-   CHGVAR VAR(&PGMLIB) VALUE(%SST(&DATA80 19 10))
2000- ENDSUBR
2100- /* END INCLUDE SRCMBR(CPY0031) SRCFILE(MYLIB/CPYSRC) */

As you see it is a lot easier to insert the lines for the variables and the include statement into multiple programs than it is to copy the subroutine into each program.

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.3, and should work for some earlier releases too.

2 comments:

  1. Hi Simon, thanks - been using the RCVMSG technique for years but this seems quick and simple. One issue I can't figure out is, when debugging, I can't see the included source. Is there a compile option or debug option to see the includes?

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.