We've all seen it "MCH3201 - Pointer not set for location
referenced." We all know what it means: (1) There is a reference to
unpassed parameter, (2) There's no recovery and (3) We better fix it quickly
before someone realizes we are a bad
programmer.
Do you remember when you first saw a Reference to an unpassed
parameter error? It wasn't in RPG, it was in CL. The solution I choice
to use was to become an expert on user-written CL commands. A CL command
is nothing more than a description of a program's parameters. It has a
prompter and syntax checker that lets you avoid passing the wrong
information to a program--in addition you get to pass default values for
parameters that the user doesn't really care about "this time".
UnPassed Parms in RPGIII
The *PARMS subfield on the PSDS (Program Status Data Structure) was
used to determine the number of parameters passed to the RPGIII program.
For example, if the program supports 3 parameters and the caller passed
in only 2 parameters, the program would load, start running and as soon
as the program "touched" that third parameter, bam! "RPG0221 -
MYPGM 1400 referenced to a parameter not passed (C G S D F)."
If you look at the low-level joblog messages, however, you will see
the MCH0801 message was actually generated first. "MCH0801-
Argument associated with external or internal parameter not passed."
To use *PARMS to avoid this kind of error, the PSDS data structure
must be included in the RPGIII program. To do that, add the Input
specifications for a Data Structure, and prefix the "DS" type with an
"S" and include a subfield where the From/To column positions are *PARMS
instead of real from/to columns. Then name the field anything you want.
Naming it PARMS will be useful when migrating to RPGIV since %PARMS can
then be easily substituted for the PARMS subfield. He is what it looks
like in RPGIII:
IPSDS SDS
I *PARMS PARMS
IWORK DS
I 1 15 IP
I I '198.162.0.101' 16 30 DFTIP
I 31 40 USRPRF
I I 'QDFTOWN' 41 50 DFTUSR
I 51 60 PWD
I I 'ROSEBUD' 61 70 DFTPWD
C MOVE *ON *INLR
C *ENTRY PLIST
C PARM RMTIP 15
C PARM USER 10
C PARM PASSWD 10
/SPACE
C PARMS IFGE 1
C MOVELRMTIP IP
C ELSE
C MOVELDFTIP IP
C ENDIF
C PARMS IFGE 2
C MOVELUSER USRPRF
C ELSE
C MOVELDFTUSR USRPRF
C ENDIF
C PARMS IFGE 3
C MOVELPASSWD PWD
C ELSE
C MOVELDFTPWD PWD
C ENDIF
C RETRN
UnPassed Parms in RPG IV
Moving to RPG IV, programmers quickly adopted the %PARMS() built-in
function to check the number of parameters passed to the program. If 2
out of 3 parameters were passed, the program could avoid "touching" the
third parameter. But if this precaution isn't taken, then RPG IV program
generated the same type of error messages, but with different numbers.
In RPG IV, touching an unpassed parameter means "CEE9901 -
Application error. MCH3601 unmonitored by MYPGM at statement 0000001800,
instruction X'0000'." Looking at the low-level joblog shows
that "MCH3601 - Pointer not set for location referenced."
message.
Here is an example in RPG IV:
D rmtIP S 15A
D IP S 15A
D dftIP S 15A
D user S 10A
D usrprf S 10A
D dftusr S 10A Inz('QDFTOWN')
D password S 10A
D pwd S 10A
D dftpwd S 10A Inz('ROSEBUD')
C MOVE *ON *INLR
C *ENTRY PLIST
C PARM RMTIP
C PARM USER
C PARM PASSWORD
/free
if (%parms() >= 1);
eval ip = rmtip;
else;
eval ip = dftip;
endif;
if (%parms() >= 2);
eval usrprf = user;
else;
eval usrprf = dftusr;
endif;
if (%parms() >= 3);
eval pwd = password;
else;
eval pwd = dftpwd;
endif;
// Do something here
return;
/end-free
Note that since RPG IV includes the %PARMS built-in function, there
isn't much need for the legacy PSDS data structure any longer.
One more note; as of v7r1 the %PARMNUM built-in function was
introduced. This built-in function allows you to specify the name of the
parameter in place of the parameter's number. Therefore you can change
the parameters sequence without the burden of having to adjust the Calc
specs. This is very useful during the development phase of the program.
It also allows you to quickly write this type of error/recovery routine
without counting the parameters on your fingers. Here is the above
example, re-written using the %PARMNUM built-in function:
H DFTACTGRP(*NO)
H OPTION(*SRCSTMT) ACTGRP(*NEW)
DTESTNOPARM PR EXTPGM('TESTNOPARM')
D RMTIP 15A
D USER 10A
D PASSWORD 10A
DTESTNOPARM PI
D RMTIP 15A
D USER 10A
D PASSWORD 10A
D IP S 15A
D dftIP S 15A
D usrprf S 10A
D dftuser S 10A Inz('QDFTOWN')
D pwd S 10A
D dftpwd S 10A Inz('ROSEBUD')
C MOVE *ON *INLR
/free
if (%parms() >= %parmNum(rmtIP));
ip = rmtIP;
else;
ip = dftIP;
endif;
if (%parms() >= %parmNum(user));
usrprf = user;
else;
usrprf = dftUser;
endif;
if (%parms() >= %parmNum(password));
pwd = password;
else;
pwd = dftPWD;
endif;
// Do something here
return;
/end-free
UnPassed Parms in CL
As I mentioned, unpassed parameter errors were often handled with
user-written CL commands. While that is the correct solution, it is not
the only one. Around about 1985 IBM's Jim Sloan showed us all
how to capture unpassed parameter error message in CL using a simple MONMSG
statement. When message MCH3601 would be raised, you trap it and
continue processing. Pretty cool! Here is an example:
DFTPARMS: PGM PARM(&RMTIP &USER &PASSWORD)
DCL VAR(&RMTIP) TYPE(*CHAR) LEN(15)
DCL VAR(&USER) TYPE(*CHAR) LEN(10)
DCL VAR(&PASSWORD) TYPE(*CHAR) LEN(10)
DCL VAR(&IP) TYPE(*CHAR) LEN(15)
DCL VAR(&USRPRF) TYPE(*CHAR) LEN(10)
DCL VAR(&PWD) TYPE(*CHAR) LEN(10)
DCL VAR(&DFTIP) TYPE(*CHAR) LEN(15) +
VALUE('192.168.0.101')
DCL VAR(&DFTUSR) TYPE(*CHAR) LEN(10) +
VALUE('QDFTOWN')
DCL VAR(&DFTPWD) TYPE(*CHAR) LEN(10) +
VALUE('ROSEBUD')
CHGVAR VAR(&USRPRF) VALUE(&USER)
MONMSG MSGID(MCH3601 MCH0801) EXEC(DO)
CHGVAR VAR(&USRPRF) VALUE(&DFTUSR)
ENDDO
CHGVAR VAR(&PWD) VALUE(&PASSWORD)
MONMSG MSGID(MCH3601 MCH0801) EXEC(DO)
CHGVAR VAR(&PWD) VALUE(&DFTPWD)
ENDDO
CHGVAR VAR(&IP) VALUE(&RMTIP)
MONMSG MSGID(MCH3601 MCH0801) EXEC(DO)
CHGVAR VAR(&IP) VALUE(&DFTIP)
ENDDO
FTPFILE RMTSYS(&IP) PUTGET(*GET) LCLFILE(&USRPRF/QXMLSRC) +
LCLMBR(CONFIG) USER(&USRPRF) PWD(&PWD)
ENDPGM: ENDPGM
Strictly speaking, you probably don't need the MCH0801 monitor,
but I like to be complete. If you want to be a bit lazy, you can
probably monitor for just MCH0000 as monitoring is generic; that is any
messages you monitor that end in 0 (zero) treat the zeros as a generic
symbol, so MCH3600 monitors for all messages that begin with "MCH36"
meaning MCH3600 through MCH36FF are trapped.
Pretty cool, huh?
Call Me
Bob Cozzi has been providing the solutions to midrange problems,
in the form or articles and books since 1983. He is available
for consulting/contract development or on-site RPG IV, SQL, and CGI/Web training.
Currently many shops are contracting with Cozzi for 1
to 3 days of Q&A and consulting with their RPG staff. Your staff gets to ask real-world
questions that apply to their unique development situations. To contact Cozzi,
send an email to: bob at rpgworld.com
You can subscribe to RPG Report (we call it "follow") by
visiting the RPG Report
page
on midrangeNews.com and then click the FOLLOW link in the table of contents
for that
page. To unsubscribe, simply click that same link. You must be
signed up and signed
in to midrangeNews.com to start following RPG Report.
Follow Bob Cozzi on Twitter