Safely and Reliably Passing CL Parameters
by Dan Riehl
Copyright 1995,1996 Powertech Toolworks, Inc.
No portion of this document may be reproduced in any form unless it carries the copyright notice intact. As long as
the copyright notice is included, you are free to reproduce the
document for your own personal use. Powertech Toolworks, Inc assumes no
liability for inconsistencies or errors found, or arising from
the use, or misuse of this educational material.
Most children learn at an early age that "it's nice to share."
Although sometimes that's a hard concept to grasp, kids eventually
realize the sandbox is a more harmonious place when everyone shares
their toys.
The notion of sharing also helps computer programs work in harmony,
passing information back and forth in a smooth flow. Although
you can easily use and manipulate program variables in the CL
program in which they're defined, it's also not too difficult
to code a program that will share data with a program it calls.
When you specify PARM values with the CALL (Call Program) or TFRCTL
(Transfer Control) command, the calling program passes those values
to the called program, which can then use and manipulate them.
If you keep a few simple principles in mind, you can code your
CL programs to simply and reliably pass all types of data. This
month, we'll examine the rules that govern parameters in CL programs.
A value a calling program passes to a called program is commonly
called a program parameter. You specify a program parameter by
indicating a value or variable name for the PARM keyword in the
calling program's CALL or TFRCTL command and in the called program's
PGM (Program) command.( TFRCTL Considerations offers a few details about
using TFRCTL to pass parameters). A program parameter can be a program variable or a literal.
Figure 1 shows how to use a program variable as a program parameter.
PGMA declares variable &Name and then calls PGMB, specifying
&Name as a parameter. For the call to succeed, PGMB must be
able to accept the passed value, so PGMB's PGM command must specify
the name of a variable into which the passed value can be placed,
as the figure also shows. Of course, this variable must also be
declared in PGMB, and it can be used just like any other variable
in that program.
The calling program's PARM parameter can also contain a list
of variables. When it does, the called program is passed the same
number of values, each in a separate parameter. For example, the
following PGM command lists two input parameters, &Cusnbr
and &Cusname:
Being passed a variable's value has the same effect in the called
program as assigning the variable an initial value. Because the
calling program is already passing an initial value, you can't
assign an initial value to an input variable in a called program.
Defining Parameters in a Called Program
Because CL supports only three types of variables -- character
(*CHAR), decimal (*DEC), and logical (*LGL) -- it follows that
each input variable in the called program must be defined as one
of those types. The input variables in the following example comprise
one of each type:
PGM PARM(&Name &Number &Flag)
DCL &Name *CHAR 9
DCL &Number *DEC (15 5)
DCL &Flag *LGL
Furthermore, you must code the parameters in the PGM command
in the same order in which they're passed. You must specify the
parameters in the correct order in the CALL command; if you don't,
the data types of the parameters would be inconsistent between
the CALL and PGM commands, and the call would fail. The DCL commands
in the calling and called programs can be in any order. For the
preceding example, you might write the CALL command in the calling
program like this:
Passing Literals to a Called Program
To pass literals to a called program, code them in the PARM parameter
of the CALL command. When you specify one parameter, you can code
the PARM keyword positionally:
You can specify up to 50 parameters. When you list more than one,
you must enclose the list in parentheses, as with all list-type
values:
Parameters passed from an AS/400 command line must always be literals,
because variables can exist only in a program.
Passing Character Literals and Logical Constants
When character literals are passed, the called program must declare
the input variables as *CHAR types. The program called in the
last example, then, would contain the following commands:
Notice that the variables are declared as nine-byte *CHAR variables,
even though two of the literals (Monday and Tuesday) contain fewer
than nine characters. When you declare the variables used as parameters,
it's wise to make them long enough to hold the longest value that
might be passed to them.
When a *CHAR variable is longer than the value being passed to
it, the value is left-adjusted and padded to the right with blanks.
In the preceding example, the values of variables &day1, &day2,
and &Day3 would be Monday, Tuesday, and Wednesday, respectively
( indicates trailing blanks).
If the passed literal is longer than the input variable, the
extra characters are truncated. In the following example, if the
variable declarations in the called program are the same as those
in the last example, each variable will contain only the first
nine characters ("Today is ").
The remaining characters would be truncated. Note that CL does
not return an error message when it truncates a passed parameter.
Logical constants are passed by the same rules as character literals.
When you use the logical constants '0' and '1' as parameters,
the system treats them as character literals. Logical constants
are passed with a length of 32, left-adjusted and blank-padded
in the input variable.
Character Literals of Various Lengths
CL takes a flexible approach to the lengths of passed literals.
All character literals of fewer than 32 characters are passed
with a length of 32. If necessary, the literal is padded with
blanks before it's passed. A character literal of more than 32
characters is passed in its exact length.
Figure 2 illustrates this point. The first four literals are
passed with lengths of 32 characters. The fifth, which is longer
than 32 characters, is passed in its full length (47 characters).
As we've said, when an input variable is shorter than the parameter
passed, the extra characters are truncated. In the figure, all the
truncated characters are blanks.
But another problem can occur with input variables of more than
32 characters, as Figure 3 illustrates. The calling program passes
the first parameter with a value of "Here is a potential
problem", blank-padded to 32 characters. The called program,
however, declares the corresponding input variable to have a length
of 40 (see &Parm1). The passed parameter value fills 32 of
the 40 positions, leaving eight unfilled. These remaining eight
positions are filled with the first eight characters of the next
parameter. To avoid this problem, when passing character literals
to an input variable more than 32 bytes long, you should blank-pad
the literal up to the length of the input variable. Note that
the second parameter is received correctly; it doesn't start where
it left off in &Parm1.
Passing Decimal Literals
You can also use decimal literals as parameters from an AS/400
command line or a CL program:
When you pass decimal literals, the called program must declare
the corresponding input variables as *DEC (15 5) (if it doesn't,
the called program will abort with a decimal data error).
CL passes decimal literals as packed-decimal data with the decimal
point properly aligned in the input variable. Figure 4 shows how
CL handles the parameters from this example. (For another method
of passing decimal values, see Using Hexadecimal Notation).
Passing Variables to a Called Program
In addition to passing literals as parameters, you can pass variable
values from a CL program. For example:
The first example passes the current value of variable &name
to the called program. The second example passes the current values
of three program variables. The third example passes a combination
of program variables and literals to PROGRAM3.
When you use variables as program parameters, you must, of course,
declare the variables in the calling program, as in the following
example:
The variable names in the calling and called programs don't need
to match, but the data types and lengths must be consistent. Note
that you don't need to define decimal variables to be passed (e.g.,
&department) as (15 5), as you did when passing a decimal
literal. Rather, you define each decimal variable to match the
data type and length of the corresponding program variable to
be passed.
The DCL commands in the calling and called program can be in
any order and do not have to declare the variables with the same
names. The following example shows how PROGRAM2 might look:
As required, the parameters appear in the same order as they do
in PROGRAM1's CALL command.
The Mechanics of Passing Variables
When you declare a variable in a program, the variable is assigned
its own storage in the program automatic storage area (PASA).
When variables are passed to a called program, OS/400 doesn't
actually pass the variable's value, but rather a pointer to the
PASA. The called program then uses the pointer to retrieve and
manipulate the data. (To understand how OS/400 handles passing
parameter values when you submit a job that uses the CALL command,
see SBMJOB Considerations)
Because the calling and called programs share the storage area,
changes the called program makes to parameter variables are reflected
in the calling program. The same is not true when literals are
passed as parameters: Their values don't change in the calling
program even if the called program changes the value of the corresponding
input variable.
When you want to manipulate an input variable in the called program
but don't want the change reflected in the caller, you must localize
the variable. To localize a variable, you declare another variable
in the called program with the same data type and length as the
input variable. Then you use the CHGVAR (Change Variable) command
to assign the value of the input variable to the localized variable.
For example:
The storage areas for &Name and &Address are used by both
the calling and the called programs. The storage areas for &Namex
and &Addressx are local -- in other words, known only to the
called program. Changes made to &Namex and &Addressx won't
affect the calling program's storage area, although the calling
program will reflect any change made to &Name and &Address.
When a called program calls another program, it can pass along
any parameter that was passed to it. The variable storage area
is still shared, and all programs that use the parameter can modify
the contents of the parameter variable in the first program.
Now that you understand how CL passes parameters, you can appreciate
the cardinal rule for passing variables to a called program: The
definition (type and length) of a variable used as a parameter
should be identical in both the calling and the called program.
When you follow this rule, you can avoid many long hours of debugging.
This article first appeared in the June 1995 issue of News/400
Magazine.
All the considerations for passing parameters that apply to the
CALL command also pertain to the TFRCTL command. In addition,
you need to remember two other rules:
Pursuant to these rules, the following uses of TFRCTL are not
valid:
PGM PARM(&custnbr)
DCL &custnbr *DEC (5 0)
TFRCTL PGMB PARM('UPDATE' &custnbr)
In the first example, TFRCTL tries to pass a parameter variable
that doesn't appear in the PGM command, and so wasn't passed to
the program. In the second example, TFRCTL correctly passes &custnbr,
which appears in the PGM command, but it also tries to pass the
constant UPDATE.
These rules may seem restrictive, but they're logical when you
think about it. By requiring that program parameters are passed
initially to the program that executes the TFRCTL command, you
ensure that the storage area for those parameters still exists
when the program containing the TFRCTL command ends.
Back to article
Using Hexadecimal Notation
When you test or debug a program that receives parameters, you
may have occasion to call the program from a command line, and
from the command line, you can pass only literals for parameter
values. When the program you call requires a decimal-type parameter,
you can define the input variable in the called program as *DEC
(15 5) and then call the program from the command line using a
decimal literal (e.g., 425.6, 5164.23, 000000263100000). However,
you may want to define the decimal field with a different length
than (15 5). But then you wouldn't be able to call that program
from the command line using a decimal literal.
You can avoid this problem by using hexadecimal notation to specify
the literal in the call. Hex notation lets you emulate the packed-decimal
format in which CL decimal literals are passed. For instance,
passes the decimal literal 5 to a program that accepts the parameter
into a variable defined as *DEC (15 5). This is the same as specifying
You can also use hex notation to pass decimal parameters not
defined as *DEC (15 5). Consider:
If you define the input variable as *DEC (5 4), the called program
sees the decimal literal as 3.1416. If you define the input variable
as *DEC (5 0), the literal is seen as 31416.
There are a few rules to keep in mind when using hex notation
to emulate packed decimals.
Normally, you use hex notation to emulate packed decimals only
when testing or debugging from a command line, to give you the
flexibility of not having to declare your decimal variables as
*DEC (15 5). In a program, however, the need for hex notation
is eliminated, because programs can pass decimal variables other
than those defined as *DEC (15 5).
Back to article
SBMJOB Considerations
The SBMJOB (Submit Job) command lets you specify a command to
be executed in batch. You can specify CALL as the command to be
executed, and you can pass parameters in the CALL command:
This example submits to batch a job that calls PROGRAMA and passes
it two parameters: &Name and &Number.
Remember that when variables are used as parameters in the CALL
command, OS/400 passes a pointer to the calling program's PASA,
rather than passing the actual value of the variable. If this
were also true for a submitted batch job, the batch program would
try to reference the interactive job that submitted it. But if
the batch job weren't executed immediately, that pointer might
no longer be valid (e.g., if the interactive user signed off or
started a different program).
But the CALL command works differently with the SBMJOB command
than it does in a CL program or from the command line. When you
use the SBMJOB command, variables are automatically translated
into literals when the job is submitted. The following example
shows the effect of the SBMJOB command:
SBMJOB +
CMD(CALL PROGRAMB PARM(&Name &Number))
RETURN
ENDPGM
The actual command submitted for batch processing is:
The automatic translation of variables to literals makes the
CALL command quite flexible, but it also means you must follow
the rules discussed in the article for passing literals as parameters.
Assume PROGRAMB's incoming parameters are declared with the same
length and data type as they are in PROGRAMA (i.e., with &number
defined as *DEC (5 2)). Remember that when you pass a decimal
literal as a parameter, the input variable must be declared as
*DEC (15 5). When the batch job begins, a decimal data error will
occur because the job expects a parameter defined as *DEC (5 2)
but gets one defined as all decimal literal parameters are: *DEC
(15 5). For PROGRAMB to work, you must define the input variable
as *DEC (15 5).
PGM PARM(&Cusnbr &Cusname)
CALL MYPROGRAM +
PARM('Christine' 22.5 '1')
CALL MYPROGRAM 'Monday'
CALL MYPROGRAM +
PARM('Monday' 'Tuesday' +
'Wednesday')
PGM PARM(&Day1 &Day2 &Day3)
DCL &Day1 *CHAR 9
DCL &Day2 *CHAR 9
DCL &Day3 *CHAR 9
CALL MYPROGRAM +
PARM('Today is Monday' +
'Today is Tuesday' +
'Today is Wednesday')
CALL MYPROGRAM +
PARM(5 3.1416 129.95)
PGM PARM(&Number1 &Number2 &Number3)
DCL &Number1 *DEC (15 5)
DCL &Number2 *DEC (15 5)
DCL &Number3 *DEC (15 5)
CALL PROGRAM1 PARM(&Name)
CALL PROGRAM2 PARM(&Name &Address &Phone)
CALL PROGRAM3 PARM(&Radius 3.1416 &Area)
PROGRAM1: PGM
DCL &Name *CHAR 10
DCL &Department *DEC (5 0)
CHGVAR &Name 'Mary'
CALL PROGRAM2 +
PARM(&Name &Department)
RETURN
ENDPGM
PROGRAM2: PGM PARM(&Alpha &Dept)
DCL &Dept *DEC (5 0)
DCL &Alpha *CHAR 10
IF (&Alpha = 'Mary') DO
CHGVAR &Dept VALUE(12600)
ENDDO
RETURN
ENDPGM
PGM PARM(&Name &Address)
DCL &Name *CHAR 20
DCL &Address *CHAR 35
DCL &Namex *CHAR 20
DCL &Addressx *CHAR 35
CHGVAR &Namex &Name
CHGVAR &Addressx &Address
AS/400 CL Parameter Additional Information
TFRCTL Considerations
PGM
DCL &custnbr *DEC (5 0)
TFRCTL PGMB PARM(&custnbr)
CALL MYPROGRAM PARM(X'000000000500000F')
CALL MYPROGRAM PARM(5)
CALL MYPROGRAM PARM(X'31416F')
PGM
DCL &Name *CHAR 10
DCL &Number *DEC (5 2)
.
. /* manipulate variables */
.
SBMJOB +
CMD(CALL PROGRAMA PARM(&Name &Number))
RETURN
ENDPGM
PROGRAMA: PGM
DCL &Name *CHAR 10
DCL &Number *DEC (5 2)
CHGVAR &Name 'Bobolink'
CHGVAR &Number 129.95
CALL PROGRAMB +
PARM('Bobolink' 0000000129.95000)