Wednesday, March 25, 2015

Getting off the RPG cycle

no rpg cycle with main and no main procedures

The cycle is still a part of RPG, and while you are not using it in your RPG programs it is still there lurking. I have always wished there was a way to be able to turn it off, and allow RPG to be linear. In recent releases this has started to happen.

With the file definitions in all free RPG it is not possible to code a file as primary or secondary, or to define matching records and level break indicators. But the cycle is still there. For example, the program below uses the cycle to read all the records from a file:

  dcl-f TESTFILE keyed ;

  read TESTFILER ;
  if (%eof) ;
    *inlr = *on ;
    dsply 'End of file' ;
  else ;
    dsply FLD001 ;
  endif ;

There are four records in TESTFILE, therefore, the output produced looks like:

  DSPLY  1
  DSPLY  2
  DSPLY  3
  DSPLY  4
  DSPLY  End of file

So while you would expect my program of nine lines to be very small, it is not as all of the RPG cycle stuff is included too. If I could create linear programs I could create smaller program objects.

IBM i 6.1 gave us the MAIN and NOMAIN keywords for the control options/H-specs. When the object is compiled, and one of these keywords is present, none of the RPG cycle functionality is included within it.

NOMAIN should be placed at the top of the source member for external subprocedures, as when it is compiled the module produced does not contain a Program Entry Procedure, PEP, which means it is not a callable program.

    ctl-opt nomain ;

 /define GetEmployeeName
 /include devsrc,testrpg_c
 /undefine GetEmployeeName

    dcl-proc GETEMPLOYEENAME export ;
      dcl-pi *n char(85) ;
        EmplNbr char(10) options(*nopass) value ;

    end-proc ;

If you are interested in learning more about coding subprocedures read the post More about subprocedures.

Coding a MAIN procedure is pretty much the same as coding any other procedure. It has a Procedure prototype (DCL-PR), Procedure interface (DCL-PI), and the Procedure (DCL-PROC) itself:

What I feel is the biggest limitation of using a MAIN is that it is not possible to return a value to the calling program.

01  ctl-opt main(Main) ;

02  dcl-pr Main extpgm('TESTRPG') ;
03    *n char(1) ;
04  end-pr ;

05  dcl-proc Main ;
06     dcl-pi *n ;
07       PassedKey char(1) ;
08     end-pi ;

09     dcl-f TESTFILE keyed ;
10     dcl-ds TestData likerec(TESTFILER) ;

11     chain Passedkey TESTFILER TestData ;
12     if (%found) ;
13       dsply TestData.FLD003 ;
14     else ;
15       dsply 'Key not found' ;
16     endif ;
17  end-proc ;

The first thing to notice is the control option, line 1, MAIN followed by the name I am going to call the procedure Main.

When defining the Procedure prototype I must use the EXTPGM keyword, line 2, to contain the name of the program that this procedure is contained within, TESTRPG. I will be passing a one character parameter to the procedure, line 3, which I am not naming here. And the prototype ends on line 4.

The start of the Procedure is marked by the DCL-PROC, line 5. I have not bothered to give the name of the procedure in the Procedure interface, line 6, so I have to need to have *N there to indicate that I am not. On line 7 I have given the passed parameter a name, and the interface ends on line 8.

Line 9 is a file definition. If a file is defined within a procedure I have to read the file into a data structure. On line 10 I define the data structure using the LIKEREC keyword. This defines all the fields in the record format, TESTFILER, to be subfields in the data structure.

Line 11 shows the CHAIN operation using the passed parameter as the key. The retrieved record is moved into the data structure, which is given after the record format or file name. As this is only one move from the input buffer this is faster than moving the individual fields from the input buffer, although with many files the difference is negligible.

All of the subfields in the data structure automatically qualified, see line 13.

The procedure ends on line 17. Notice how there is no RETURN operation code and no *INLR.

When compiled this program will contain no RPG cycle.

The fixed format definition equivalent of this program would look like:

01  H main(Main)

02  D Main            PR                  extpgm('TESTRPG')
03  D                                1

04  P Main            B

05  FTESTFILE  IF   E           K DISK

06  D                 PI
07  D  PassedKey                     1

08  D TestData        DS                  likerec(TESTFILER)
09    chain Passedkey TESTFILER TestData ;
10    if (%found) ;
11      dsply TestData.FLD003 ;
12    else ;
13      dsply 'Key not found' ;
14    endif ;
15  P                 E

If you want to learn more about all free RPG you should check out the following posts:


You can learn more about these on the IBM website:


This article was written for IBM i 7.2, and it should work with 6.1 and greater too.


  1. I don't understand the complaint "What I feel is the biggest limitation of using a MAIN is that it is not possible to return a value to the calling program."

    If you feel that's important - don't use a program - use a service program or include the module with your program and simply use procedures. For that to be a "limitation" is, IMO, silly. It's not a limitation because there are mechanisms that allow you do exactly what you to do just that.

    If you want it to be a self contained program and still return a value you can do it via parameters - just like you would with ANY program from V1 to V7.2.

  2. Thanks for letting us know about an H-specification that help limit us using the RPG cycle in our programs.

  3. One other note - cleanup is now on the programmer - for example the files have to be specifically opened and closed. If the program dies before you close the files for some reason, it will generate an error message indicating the file is already opened.

    1. If you define the files within the procedure they are used you do not have to open or close them. The program still does that for you.

      If you get an error, for example writing a duplicate key record to the file, the file is still closed.

      If the file is not defined in the procedure but at the "top" of the program, like you would have to do in IBM i 6.1, then yes you do have to open and close the file, and the file can be left open after an error.

  4. What about global variables in programs defined with MAIN? And the impact of Activation Groups?

    I've been experimenting with MAIN more and more. I try to limit global variables - but do occasionally use them. I've noticed that they retain their value when the program is called again.

    This makes me think that I need a better understanding of activation groups.

  5. To future proof your code, should add (n) no-lock to the chain. If another developer changes your F-spec to update, will needlessly be getting a record lock which could cause issue for other jobs. Yes the compiled listing will have a warning but I just ignore it.



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.