Data Structures Make Good Status Parameters
October 31, 2012 Ted Holt
When a good program goes bad, someone must fix it. Sometimes that someone is me. As for the “when,” it’s never a good time. It’s important to me that the failing program give me as much information as possible to help me pinpoint the cause of the error as quickly as possible.
There are two ways a program can inform its caller that it could not complete normally. One way is by sending a message. (I have written about this topic before; see the Related Stories at the end of this article.) The other way is to use a status parameter, also known as a “return code.”
It is common for a program to place a zero into a status parameter if all went well, and a non-zero value if something went wrong. A good example of this convention is the SQL state.
I wrote earlier this year about an extension of the SQL state that identifies the statement that ended in error.
You can take this idea even further, making the program in error pass back as much data as you wish. I illustrate with an RPG data structure, but the technique works with any language.
First, create a message, like this:
crtmsgf *curlib/mymsgf addmsgd msgid(MYM1101) msgf(*curlib/mymsgf) + msg('&2 errors were found in program &1.') + seclvl('Correct the errors and rerun.') + fmt((*char 10) (*bin 4))
The message has two positional parameters, which occupy 14 bytes of storage.
Now, define a compatible data structure in the program(s) that will trap for this error. Here’s the structure in RPG.
D Status ds 256 inz D MsgID 7a D PgmName 10a D ErrorCount 10i 0
The first seven bytes are set aside for the message ID. The remaining subfields of the data structure match the definitions of the substitution values in the message description.
I’ve kept the structure simple. It tells me the name of the rogue program and the number of errors it found. The more information you put into the structure, the better, because a user can read a message to you over the telephone, but having someone read a program dump to you is impractical.
Loading the structure is easy.
if ErrorCounter > *zero; MsgID = 'MYM1101'; PgmName = ProcName; // from the PSDS ErrorCount = ErrorCounter; ouStatus = Status; endif;
The last line gives the data back to the caller, because ouStatus is the first parameter.
D OnePgm pi D ouStatus 256a D inWhs 3a const D inItem 12a const
The driver CL looks like this (without appropriate code to handle unexpected errors).
pgm parm(&inOption &inItem &inWhs) dcl &inOption *char 1 dcl &inItem *char 12 dcl &inWhs *char 3 dcl &Status *char 256 call OnePgm (&Status &inWhs &inItem) if cond(%sst(&Status 1 7) *eq ' ') then( + call TwoPgm (&Status &inOption)) if cond(%sst(&Status 1 7) *eq ' ') then(+ call RedPgm (&Status)) if cond(%sst(&Status 1 7) *eq ' ') then(+ call BluePgm (&Status &inWhs)) if cond(%sst(&Status 1 7) *ne ' ') then( + sndpgmmsg msgid(%sst(&Status 1 7)) msgf(MyMsgF) + msgdta(%sst(&Status 8 249)) + msgtype(*escape)) endpgm
If a program finds an error, the system ignores the subsequent program calls. If any program ends in error, the last IF makes the CL program shut down by sending an escape message similar to this one:
50 errors were found in program OnePgm.
I used this technique in a project I worked on recently, and was very pleased with the results. Data structures make tremendous status parameters.