|
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 +---
As an Amazon Associate we earn from qualifying purchases.
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.