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.

CL Parameter Basics

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:


PGM   PARM(&Cusnbr  &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:


CALL MYPROGRAM             +

   PARM('Christine' 22.5 '1')

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:


CALL MYPROGRAM 'Monday'

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:


CALL MYPROGRAM             +

   PARM('Monday' 'Tuesday' +

        'Wednesday')

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:


PGM   PARM(&Day1 &Day2 &Day3)

DCL   &Day1 *CHAR 9

DCL   &Day2 *CHAR 9

DCL   &Day3 *CHAR 9

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 ").


CALL MYPROGRAM              +

  PARM('Today is Monday'    +

       'Today is Tuesday'   +

       'Today is Wednesday')

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:


CALL MYPROGRAM          +

   PARM(5  3.1416  129.95)

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).


PGM PARM(&Number1 &Number2 &Number3)

DCL &Number1 *DEC (15 5)

DCL &Number2 *DEC (15 5)

DCL &Number3 *DEC (15 5)

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:


CALL PROGRAM1 PARM(&Name)

CALL PROGRAM2 PARM(&Name &Address &Phone)

CALL PROGRAM3 PARM(&Radius 3.1416 &Area)

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:


PROGRAM1:  PGM

           DCL &Name       *CHAR 10

           DCL &Department *DEC (5 0)

           CHGVAR &Name 'Mary'

           CALL PROGRAM2          +

             PARM(&Name &Department)

           RETURN

           ENDPGM

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:


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

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:


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

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.

AS/400 CL Parameter Additional Information

TFRCTL Considerations

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:

  • Only variables can be passed as parameters; constants are not allowed.
  • Variables used as parameters must have been passed initially as parameters to the program executing the TFRCTL command.

Pursuant to these rules, the following uses of TFRCTL are not valid:


PGM

DCL       &custnbr  *DEC (5 0)

TFRCTL    PGMB      PARM(&custnbr)

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,


            CALL MYPROGRAM PARM(X'000000000500000F')

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


            CALL MYPROGRAM PARM(5)

You can also use hex notation to pass decimal parameters not defined as *DEC (15 5). Consider:


            CALL MYPROGRAM PARM(X'31416F')

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.

  • The literal must be preceded by an X and enclosed in quotes.
  • The string can contain only the numerals 0 through 9 and must end in a trailing sign (F for a positive number or D for a negative number).
  • The string must contain an even number of hex numbers. Each pair of numbers is packed into a single byte.

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:


PGM

DCL &Name     *CHAR 10

DCL &Number   *DEC (5 2)

     .

     .  /* manipulate variables */

     .

SBMJOB                                   +

      CMD(CALL PROGRAMA PARM(&Name &Number))

RETURN

ENDPGM

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:


PROGRAMA:  PGM

           DCL    &Name   *CHAR 10

           DCL    &Number *DEC (5 2)

           CHGVAR &Name   'Bobolink'

           CHGVAR &Number 129.95

SBMJOB + CMD(CALL PROGRAMB PARM(&Name &Number)) RETURN ENDPGM

The actual command submitted for batch processing is:


           CALL PROGRAMB         +

             PARM('Bobolink' 0000000129.95000)

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).

Back to article