God_of_Thunder
God_of_Thunder

Reputation: 763

How to create an AS/400 command that accepts a decimal parameter with varying length?

I need to create an AS/400 command. This command has a parameter with decimal type, which is used to return a value to a CL variable from its command processing program, which is a RPGLE program. Originally the length of the return decimal value should have length 10 with 0 decimal place, which was straightforward to do, as the length of the CL variable is also fixed. Now my boss wanted to enhance this command so that this parameter could return value to a CL variable which has a varying length (still 0 decimal place), similar to what RTVDTAARA command can do when retrieving value from a decimal data area. Can anyone tell me how to make this work? Thanks a lot.

Upvotes: 0

Views: 3728

Answers (2)

Tracy Probst
Tracy Probst

Reputation: 1859

A more complex way to do this is to make a parameter of type *X. Then you can pass any kind of variable to your RPGLE program and process it. You can accept *CHAR, *DEC, *INT, whatever. Here is an example PARM statement for a command definition:

PARM       KWD(RTNVAR) TYPE(*X) LEN(1 10 0) +
             RTNVAL(*YES) MIN(1) VARY(*YES *INT2) +
             PASSATR(*YES) Prompt('Return Field')

If you enter this and click on help, it can explain how the TYPE(*X) works with the LEN attribute and how the VARY and PASSATR attributes work. For this example, I put in a default of 10,0 for decimals, but it doesn't have to be.

Then, in your RPGLE program, take that entry parameter and break it up into a Data Structure like this:

D#EntryVariable   DS
D parmAttr1               1      1i 0
D parmNumDec              2      2i 0
D parmDecLength           3      3i 0
D parmSize                2      3i 0
D EntryValue              4     19

You would set that EntryValue to whatever length you feel would be the maximum keeping in mind that decimal variables in CL are stored in packed format in memory, so a 15-byte decimal field would be 8 bytes long (7.5 bytes + .5 byte sign). The data structure breakdown of the fields is as follows:

  • parmAttr1 tells you what type of variable was passed. Here are some values:
    • *DEC = 0x03
    • *INT = 0x00
    • *CHAR = 0x04
  • parmNumDec is only valid for *DEC variables. It is the number of decimals.
  • parmDecLength is only valid for *DEC variables. It is the total number of digits for the *DEC field
  • parmSize is only valid for non-*DEC variables. It is the length of the variable that was passed.
  • EntryValue is the value that is to be returned to the calling program. It will hold whatever value was in there before your command was called. Give it enough space to hold however big of a variable you will expect.

Then you process your code to retrieve a value and place it in the EntryValue part of the data structure. Be very careful when you update the EntryValue. Any data beyond the length passed does not belong to your program and if you change it, you could break something else in the job. Again, remember that CL decimal variables are stored in memory in the packed decimal format.

To populate a decimal value into an unknown sized packed field, you can do it one of two ways. The easy but tedious way to do it is to create 15 different packed decimal variables and define them in the entry parameter data structure using SELECT and WHEN to populate. The other way is to use pointers and looping. I'll show you how. To start with, you'll need some pointers and work fields defined:

 *Working field to hold the value to be returned.
 *Decimal places are implied, but not necessary.
DworkDec          S             15P 0
 *Pointer to the value part of the entry parameter.
DptrParmValue     S               *
 *Pointer to the working decimal field.
DptrWorkDec       S               *
 *A single byte of the parm value. This will move along the
 *parm value field as ptrParmValue changes.
DparmChar         S              1    Based(ptrParmValue)
 *A single byte of the workDec field. This will move along
 *the field as ptrWorkDec changes.
DworkChar         S              1    Based(ptrWorkDec)
 *A counter for positioning the ptrParmValue pointer.
DparmPos          S              5i 0
 *A counter for positioning the ptrWorkDec pointer.
DworkPos          S              5i 0
 *The actual length, in bytes, of the return parm value
DparmRealLen      S              5i 0

What you will do is move the result value into the workDec field. I suggest creating a temporary integer field (5i 0) and multiply the result by 10 to the power of the number of decimal positions in the result to get a whole number with an implied decimal and then EVAL the temporary integer field into workDec. workDec is defined as 15,0 since that's all the bigger a CL decimal field can be--keeping in mind that we are ignoring decimals at this point. So we know workDec will always be larger, or as large as the passed parameter.

After workDec is set, you start backwards from the rightmost part of both workDec and parmValue dropping the bytes in as you go. We do this because packed decimal values are stored right-justified in memory.

/free
 workPos = (%Size(workDec)+.5)/2 + .99 - 1;
 parmRealLen = (parmDecLength_.5)/2 + .99;
 For parmPos = parmRealLen-1 downTo 0;
   ptrParmValue = %Addr(parmValue)+parmPos;
   ptrWorkDec = %Addr(workDec)+workPos;
   parmChar = workChar;
   workPos = workPos-1;
 EndFor;
/end-free

Upvotes: 2

John Y
John Y

Reputation: 14559

JamesA and X-Zero have basically covered this, but just to be clear: CL programs and AS/400 commands don't have a notion of varying length decimal type. You have to pick some maximum size, and that's it.

If you actually try calling the RTVDTAARA command, which you are trying to model your command after, you will see that you can use any size decimal variable in the calling CLP to hold the return value. The only situation in which the size is a problem is when the value being returned is too large to fit the variable meant to hold the return value.

So, for example, if you have a *DEC (10 0) data area with a value of 500, you can retrieve its value using RTVDTAARA into any CL variable of type *DEC as long as that variable is (3 0) or larger. If the data area has a value of 1000, the receiving CL variable has to be (4 0) or larger.

In that sense, varying length decimal parameters are kind of built in. The standard practice is to always make the receiving CL variable the same size as whatever you are receiving from, to ensure that you can receive any value that is returned.

Upvotes: 1

Related Questions