× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.


  • Subject: RE: CL Problems - Parameter Passing
  • From: "Wills, Mike N. (TC)" <MNWills@xxxxxxxxxxxxxx>
  • Date: Thu, 17 May 2001 15:23:14 -0500

Wow! So what you are basically saying is that I shouldn't have a problem if
I use variable lengths less than or equal to 32, but I must use longer, to
make sure they are the same size? (Which by the way was the case) 

Thanks for the background information!

Mike

-----Original Message-----
From: John Taylor [mailto:john.taylor@telusplanet.net]
Sent: Thursday, May 17, 2001 2:06 PM
To: MIDRANGE-L@midrange.com
Subject: Re: CL Problems - Parameter Passing


Hi Mike,

I'll start at the beginning, and try to explain what is happening here.
(Warning: this is LONG.)

CL Parameter Basics
----------------------
When a variable is declared within a CL program, the system assigns storage
for that variable within the program automatic storage area (PASA). If you
subsequently use the variable as a parameter within a CALL command, the
system does not pass the value of that variable to the called program, but
rather a pointer to the PASA of the calling program. This is known as
parameter passing by reference.

For this reason, it is very important that both programs declare the
parameter to be of the same type and size. To illustrate, let's look at the
following example:

PgmA: Pgm

  DCL  &Var1        *CHAR    2  Inz( 'AB' )
  DCL  &Var2        *CHAR    2  Inz( 'YZ' )

  Call PgmB Parm( &Var1 &Var2 )

EndPgm

PgmB: Pgm Parm( &i_Var1 &i_Var2 )

  DCL  &i_Var1      *CHAR    4
  DCL  &i_Var2      *CHAR    2

EndPgm

Hopefully, you've noticed that the first parameter is declared to be larger
in PgmB than it was in PgmA. Although you might expect &i_Var1 to contain
'AB  ' after the call, the following is what the input parameters in PgmB
actually contain:

&i_Var1 = 'ABYZ'
&i_Var2 = 'YZ'

It shows the contents of the first parameter, and the second, because the
second parameter is immediately adjacent to the first within the storage
area. If the second parameter was not contiguous to the first, then the last
two bytes of &i_Var1 would show whatever happened to be in the storage area
at that time.

You can think of &i_Var1 as a 4-byte "window" into the storage area of the
calling program. It's passed a pointer that tells it where the view begins,
and it accesses anything in storage from that point up to the parameter's
declared length.

Looking at Literals
-------------------
There are several ways that a program can be called, other than from another
program. Examples include the command line, SBMJOB, job scheduler etc. In
the case of an interactive call from the command line, you specify the
parameters as literals, ie:

Call  PgmB Parm('AB' 'YZ')

Consider that when we do this, there is no PASA. We'll look at the
implications of that in a minute, but for now, just make a note of it.

Submitting a job from the command line isn't any different. If you're
submitting a CALL, then you'll be specifying any associated parameters as
literals. However, things can get a bit deceiving when you submit a job from
_within_ a program, as the following example illustrates:

PgmC: Pgm

  DCL  &Var1        *CHAR    2  Inz( 'AB' )
  DCL  &Var2        *CHAR    2  Inz( 'YZ' )

  SbmJob Cmd(Call PgmB Parm( &Var1 &Var2 ))

EndPgm

Clearly, we're not passing literals here. Or are we?

Let's think about how things would work if we passed variables:

- PgmC submits a call to PgmB, passing two variables as parameters.
- PgmC immediately ends as a result of the EndPgm statement.
- PgmB begins running in batch and receives pointers to PgmC's PASA.
- PgmB crashes when it attempts to use the pointers.

We have invalid pointers because PgmC is no longer running. Now, if you've
ever tried this personally, you know that it doesn't happen in practice. The
reason for that is that the system is converting those variables to literals
before issuing the CALL command. Very sneaky, but effective.

Now that we've seen some examples of where literals are used, and why, it's
time to talk about the PASA again. When we discussed the basics of CL
parameter passing, we learned that the called program expects to receive a
pointer to a storage area within the PASA for each input parameter. This
requirement hasn't changed. So here we have the CALL command passing
literals, and the called program expecting pointers. (I think I've just
found a reason to use the term "impedence mismatch" to describe something.
:)

Obviously, it's time for the system to peform some more magic behind the
scenes. In order to accomodate the requirements of the called program, the
system creates a space in temporary storage for each literal being passed,
and moves the value of the literal into that storage space. Now it can pass
pointers to the called program, and everyone is happy.

Except you that is, because none of this changes the fact that you're
getting "garbage" in the input variables of your called program! Fair
enough. I'm getting to that now, but you needed to learn some background in
order to understand the next part.

Sizing It All Up
----------------
Now that you know the system is creating variables behind the scene, you
might wonder how it knows what size those variables need to be. The answer
is that it doesn't. Instead, the designers have imposed some specific rules
about how literals are transformed to variables, and thereby passed as
parameters.

CL supports only three basic data types: character, decimal, and logical.
For the purposes of this discussion, you can consider the logical data type
equivalent to the character type, because it's treated in the same manner.

The simplest rule is the one that handles decimal literals. All decimal
literals will be converted to packed decimal format with a length of (15 5),
where the value is 15 digits long, of which 5 digits are decimal places.
Therefore, any program that you expect to call from the command line, or
SBMJOB etc., needs to declare it's numeric input parameters as *DEC(15 5).

Character literals are a little bit more complicated, but not much. The
rules are that any character literal up to 32 characters in length will be
converted to a 32 byte variable. The value is left justified, and padded on
the right with blanks.

So if you were to pass the following literal:

    Call PgmB 'AB'

the associated storage space for that literal would contain:

'ABxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

where "x" represents a blank space.

Character literals that are longer than 32 bytes are converted to a variable
of the same length as the literal value itself, as in the following examble:

    Call PgmB 'This is a long character literal that will exceed 32 bytes.'

the associated storage space for that literal would contain:

'This is a long character literal that will exceed 32 bytes.'

Finally, since the logical data type follows the same rules as the character
type, and the only possible values for a logical data type are '0' or '1',
we know that a logical literal will always be created as a 32 byte, left
justified, padded character variable.

Parameter Problems
---------------------
In the beginning of this explanation, you learned that it was important for
the parameter declarations to match between a called program and it's
caller. Then you discovered that the system sometimes has to take it upon
itself to declare the parameters of the caller on your behalf. If the two
declarations don't match, we have the potential for trouble.

In the case of a decimal value, the result is immediate and obvious; you get
a data decimal error. Character variables are more difficult to debug
because they don't generate any immediate errors. What actually happens
depends upon the length of the parameter in the called program.

If the length of the parameter in the called program is _less than_ the
length of the parameter being passed, the extra characters are effectively
truncated, as follows:

    Call SomePgm ('ABCDEFG') /* system creates 32 byte *CHAR */

SomePgm: Pgm Parm( &i_Var1 )

    DCL  &i_Var1        *CHAR    4

EndPgm

What happens is that the system passes 'ABCDEFGxxxxxxxxxxxxxxxxxxxxxxxxx'
( 'x' is a blank), but because of the declared length of &i_Var1, SomePgm
only see's 'ABCD'. To most of us, this is the behaviour that we would
expect.

Things get nasty when the declared length of the variable is _longer than_
what is being passed in. Using the same example as we've just seen above:

SomePgm: Pgm Parm( &i_Var1 )

    DCL  &i_Var1        *CHAR    34

EndPgm

In this case, the system will still allocate 32 bytes of storage and assign
'ABCDEFGxxxxxxxxxxxxxxxxxxxxxxxxx' to it, but because &i_Var1 is now
declared to be 34 bytes long, SomePgm will see more storage than it was
intended to. It will see the 32 bytes that were allocated for it, _plus_ two
additional bytes. It's those two additional bytes that can cause the
infamous "unpredictable results" which IBM's documentation often refers to.
If the extra bytes contain blanks, chances are that you won't be the wiser,
but if they contain something else, your input parameter will contain
"garbage".

As you can see by now, when dealing with literals, the magic number for
character parameters is 32. If the called program declares the parameter to
be less than or equal to 32, you'll never see "garbage" in the parameter.
Once you cross that 32 byte threshhold, you need to take extra care to
ensure that the size of the literal being passed is equal to the declared
size of the input parameter.


In conclusion, I hope that clears it up for you, Mike.


John Taylor
Canada


> ----- Original Message -----
> From: "Wills, Mike N. (TC)" <MNWills@taylorcorp.com>
> To: <MIDRANGE-L@midrange.com>
> Sent: Thursday, May 17, 2001 9:24 AM
> Subject: RE: CL Problems
>
>
> > I have read the link you sent me but one thing doesn't make sense to me.
> > Does the OS and/or the command ignore the asterisk?
> >
> > Mike
> >
> > -----Original Message-----
> > From: John Taylor [mailto:john.taylor@telusplanet.net]
> > Sent: Wednesday, May 16, 2001 5:38 PM
> > To: MIDRANGE-L@midrange.com
> > Subject: Re: CL Problems
> >
> >
> > Mike,
> >
> > This is such a common question that one of us really should put it in
the
> > FAQ. Here is one of many explanations from the archives:
> >
> > http://archive.midrange.com/midrange-l/199809/msg01310.html
> >
> >
> > John Taylor
> > Canada
> >
> > ----- Original Message -----
> > From: "Wills, Mike N. (TC)" <MNWills@taylorcorp.com>
> > To: <MIDRANGE-L@midrange.com>
> > Sent: Wednesday, May 16, 2001 16:15
> > Subject: CL Problems
> >
> >
> > > I have a RPG program that passes parameters to a CL program that
> retrieves
> > > some job attributes, then submits another CL to the jobq which will
> build
> > a
> > > PDF file and put it on the IFS. The reason we do this is so if the PDF
> > > creation crashes, it will not disrupt the job that called it. This CL
is
> > > called at the end of the job (we are being overly paranoid because
some
> of
> > > the reports do updates). My problem is this:
> > >
> > > Somehow between the RPG program and the last CL I get garbage in the
> field
> > > name causing the last CL to crash. I am filling these fields in the
RPG
> > > program, but not completely (I might be using 20 bytes of the 50 byte
> > > length). I don't want to make this smaller because the CL's are
designed
> > so
> > > many different reports can be turned into PDF files. I am wondering
how
> > that
> > > garbage can get in there. The data appears to be fine when it gets
sent
> to
> > > the first CL from the RPG program (thus why I posted here). :-) The
only
> > > ones effected are the ones that are quite long and not being
completely
> > > filled. OS version is V4R5.
> > >
> > > Thanks for any help.
> > >
> > > Mike Wills
> > > +---
> >


+---
| This is the Midrange System Mailing List!
| To submit a new message, send your mail to MIDRANGE-L@midrange.com.
| To subscribe to this list send email to MIDRANGE-L-SUB@midrange.com.
| To unsubscribe from this list send email to MIDRANGE-L-UNSUB@midrange.com.
| Questions should be directed to the list owner/operator:
david@midrange.com
+---
+---
| This is the Midrange System Mailing List!
| To submit a new message, send your mail to MIDRANGE-L@midrange.com.
| To subscribe to this list send email to MIDRANGE-L-SUB@midrange.com.
| To unsubscribe from this list send email to MIDRANGE-L-UNSUB@midrange.com.
| Questions should be directed to the list owner/operator: david@midrange.com
+---

As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.