Learn about the new ammunition available in Release 3.0
by Ernie Malaga
Upgrading your AS/400 to a new release is a time-consuming chore that you may be tempted to put off for a rainy day when you have nothing better to do. After all, spending six to eight hours waiting for messages on your AS/400 console on Sunday (or whenever) is not quite anyone's idea of fun.
But perhaps you will change your mind when you read about the new features available in RPG/400 in Release 3.0. They were long overdue, and they finally made it to your compiler.
The Initialization Subroutine
RPG/400 programs can now use a subroutine named *INZSR. This subroutine is executed automatically upon initialization of the program, right after all fields, arrays, tables, indicators, and structures are set to their initial values, but before 1P output is sent to a printer file.
What's more, the *INZSR subroutine can be called again with an EXSR operation, anytime; the *INZSR can be coded just like any other subroutine, so it can contain any operations normally available to "regular" subroutines, with the exception of the new RESET operation.
Let me give you a couple of nice applications for this subroutine. First, the system time is obtained by way of the TIME operation and, as such, cannot be calculated except after the program performs 1P output to the printer file. Since the *INZSR is executed automatically before 1P time, you can code an initialization subroutine as shown in 1. Now your O-specs can include the SYSTIM and SYSDAT fields, which will already have the system time and date, even during 1P output.
Let me give you a couple of nice applications for this subroutine. First, the system time is obtained by way of the TIME operation and, as such, cannot be calculated except after the program performs 1P output to the printer file. Since the *INZSR is executed automatically before 1P time, you can code an initialization subroutine as shown in Figure 1. Now your O-specs can include the SYSTIM and SYSDAT fields, which will already have the system time and date, even during 1P output.
Second, this is the perfect place to code all of those operations that you have to do for the first cycle only; just remember not to run an EXSR *INZSR or it will be executed all over again. For example, perhaps you store your report headings and titles in message descriptions. You could then use the *INZSR to retrieve the text from the message descriptions as shown in 2.
Second, this is the perfect place to code all of those operations that you have to do for the first cycle only; just remember not to run an EXSR *INZSR or it will be executed all over again. For example, perhaps you store your report headings and titles in message descriptions. You could then use the *INZSR to retrieve the text from the message descriptions as shown in Figure 2.
Initializing Data Structures
A data structure is a useful tool to represent a data aggregate, and a multiple-occurrence data structure is an array in which each element is a data structure. RPG/400 support for data structures, until now, lacked an easy way to initialize a data structure. When a program containing a data structure is started, the alphanumeric subfields of the data structure are initialized to blanks automatically, and so are the numeric fields. This causes decimal data errors if the numeric subfields are used before being manually initialized to zero or to some other valid numeric value.
With Release 3.0, you can code a data structure (whether internally or externally described) to instruct the system to initialize the structure subfields properly, according to their type: alphanumeric fields will be initialized to blanks, but numeric fields will be initialized to zeros.
There are two methods for initializing a data structure: globally (the entire data structure), and by individual subfields. Examples are provided in Figures 3a and 3b.
Data structure SMPLDS is initialized globally; notice the "I" coded in column 18 of the I-spec. When the program starts, fields ALPHA1 and ALPHA2 will be set to blanks, but fields NUM1 and NUM2 will be set to zero, since they are numeric. You can then use these fields with confidence, knowing that they have been properly initialized and will not cause decimal data errors.
In this case, data structure SMPLDS is not initialized globally, but on a subfield basis. When the program starts, ALPHA1 is initialized to blanks because it is alphanumeric; NUM1 is initialized to -70 (notice the "I" coded in column 8 and the numeric literal, -70, coded in column 21). Field NUM2 is initialized to blanks, even though it is numeric, so it could cause decimal data errors; field ALPHA2 is initialized to the value 'AZ'.
The CLEAR Operation
The new operation code CLEAR is used to initialize any type of variable to its proper initial value. The variable can be a field, array, array element, table, indicator, or data structure. If it is alphanumeric it will be initialized to blanks; if it is numeric, to zeros. The table in 4 summarizes the uses of the CLEAR operation.
The new operation code CLEAR is used to initialize any type of variable to its proper initial value. The variable can be a field, array, array element, table, indicator, or data structure. If it is alphanumeric it will be initialized to blanks; if it is numeric, to zeros. The table in Figure 4 summarizes the uses of the CLEAR operation.
But there is more. CLEAR can also be used on record names, thus providing an easy and convenient way to make sure that the record you are about to write is properly initialized. Before Release 3.0, you had to MOVE *BLANK or Z-ADD *ZERO on each field of the record format before you issued a WRITE operation; now you can simply CLEAR the record by name and then WRITE it. Simple? You bet.
When CLEAR is used on record names, only those fields defined as output or both are cleared; input-only fields are not affected. If you code *NOKEY in Factor 1 of the CLEAR operation, the key fields of an indexed disk file will not be cleared.
The RESET Operation
RESET is another new operation. It sets a variable back to the value it had when the initialization of the program was complete. Since this program initialization includes the *INZSR subroutine (described earlier), you can use the RESET operation to return certain variables to their "initial" values. For instance, you may have coded your *INZSR as in 5.
RESET is another new operation. It sets a variable back to the value it had when the initialization of the program was complete. Since this program initialization includes the *INZSR subroutine (described earlier), you can use the RESET operation to return certain variables to their "initial" values. For instance, you may have coded your *INZSR as in Figure 5.
This *INZSR will set field RCDTYP (record type) to the value 'CM' at program initialization. Because this is taken as the initial value of this field, issuing a RESET RCDTYP will move 'CM' to that field, automatically. You can use this operation to refresh the values of certain fields. If you use RESET on a data structure subfield that has been initialized to a specific value, that specific value will be moved to the data structure subfield automatically.
Just like CLEAR, RESET can be used on all types of variables: fields, data structures, arrays, and indicators. It can also be used on record format names and shares with CLEAR the limitations and syntax, including the *NOKEY reserved word in Factor 1.
String Operations
At long last, RPG programmers have access to string operations. Before Release 3.0, RPG programmers typically spent a great deal of time coding arrays whenever character strings had to be manipulated. With Release 3.0, these tedious coding techniques are no longer needed. RPG/400 now provides three string operations: CAT, SCAN, and SUBST.
CAT is used to concatenate two strings together. It works like the *CAT, *BCAT, and *TCAT operations in CL. You code the first string in Factor 1, the second string in Factor 2, and the resulting string in the Result Field. Optionally, Factor 2 can indicate the number of intervening blanks between the two strings to be joined.
For example, imagine that field FIRST contains your first name and field LAST contains your last name. You need to print your entire name in a "natural" way (with only one blank between names). RPG/400 can now handle this problem easily, as shown in 6.
For example, imagine that field FIRST contains your first name and field LAST contains your last name. You need to print your entire name in a "natural" way (with only one blank between names). RPG/400 can now handle this problem easily, as shown in Figure 6.
Field NAME will have both names with one blank between them, as indicated by the ":1" notation in Factor 2. This number of blanks defaults to zero if not indicated, and can be indicated by a variable too, as in LAST:N.
SCAN is a very powerful operation. Before Release 3.0 you had to CALL program QCLSCAN whenever you needed to scan a character string. This external program call is no longer necessary, resulting in a much improved performance.
For example, suppose you have a series of records (like a customer master file) and you want to find the record that has "SMITH" in the CUNAME field, as in 7. In this program segment, all records in CUSTMAST are read. The SCAN operation tries to find string 'SMITH' (which could have been stored in a field, of course) in field CUNAME. POSITN is the position in which 'SMITH' is found in CUNAME; if not found, a zero is returned. So, if POSITN is greater than zero, it is because 'SMITH' was found.
For example, suppose you have a series of records (like a customer master file) and you want to find the record that has "SMITH" in the CUNAME field, as in Figure 7. In this program segment, all records in CUSTMAST are read. The SCAN operation tries to find string 'SMITH' (which could have been stored in a field, of course) in field CUNAME. POSITN is the position in which 'SMITH' is found in CUNAME; if not found, a zero is returned. So, if POSITN is greater than zero, it is because 'SMITH' was found.
The Result Field is optional. If it is omitted, you must code an indicator in columns 58-59, which would turn on if the SCAN could not find the string.
Both Factor 1 and Factor 2 can have ":n" options. In Factor 1, ":n" indicates the length of the portion of Factor 1 to be used for scanning. In Factor 2, ":n" indicates the starting position of the scan. An example is shown in 8. This code will scan the first four characters of FIRST (the first name) in field NAME, beginning with the second character of NAME. Again, variable names could have been used instead of 4 and 2, such as FIRST:M and NAME:N.
Both Factor 1 and Factor 2 can have ":n" options. In Factor 1, ":n" indicates the length of the portion of Factor 1 to be used for scanning. In Factor 2, ":n" indicates the starting position of the scan. An example is shown in Figure 8. This code will scan the first four characters of FIRST (the first name) in field NAME, beginning with the second character of NAME. Again, variable names could have been used instead of 4 and 2, such as FIRST:M and NAME:N.
Because you can code a SCAN operation in such a way that the scanning process would go beyond the end of the character string in Factor 2, coding an indicator in columns 56-57 is advisable. This indicator will turn on if there is an error during the execution of SCAN.
SCAN has another feature. If you code a numeric array name in the result field, SCAN will assign the position of each occurrence in each element of the array. Examine the example in 9. If field NAME were to contain 'TALBOTT ATTORNEYS', the SCAN operation would change the array as seen in the same figure.
SCAN has another feature. If you code a numeric array name in the result field, SCAN will assign the position of each occurrence in each element of the array. Examine the example in Figure 9. If field NAME were to contain 'TALBOTT ATTORNEYS', the SCAN operation would change the array as seen in the same figure.
If the array has more elements than occurrences found, the first few elements are loaded with the position numbers, while the rest are cleared to zero. If the array has fewer elements than occurrences found, the array will contain the first few occurrences, ignoring those that exceed the size of the array - and there will be no warning. For example, if array ARR had only three elements, the fourth and fifth occurrence of 'T' would not be recorded and you would not even know they exist.
The SUBST operation extracts a substring from a longer string; it works like the %SST function in CL. You code the length of the desired substring in Factor 1, the base string in Factor 2, and the name of the resulting substring as the Result Field. Optionally, Factor 2 can include a ":n" notation to indicate the position where it should begin extracting the substring.
For example, to extract four characters from field LONG, beginning on the third character, and placing the result in field SHORT, as in 10.
For example, to extract four characters from field LONG, beginning on the third character, and placing the result in field SHORT, as in Figure 10.
If the ":n" notation is removed from Factor 2, the starting position is assumed to be 1 by default. As with the SCAN operation, an indicator in columns 56-57 will turn on if the SUBST operation results in an error such as attempting to go past the end of the base string. For instance, if string LONG in 10 were only five characters long, SUBST would generate an error, since it would not be able to extract four characters beginning with the third character.
If the ":n" notation is removed from Factor 2, the starting position is assumed to be 1 by default. As with the SCAN operation, an indicator in columns 56-57 will turn on if the SUBST operation results in an error such as attempting to go past the end of the base string. For instance, if string LONG in Figure 10 were only five characters long, SUBST would generate an error, since it would not be able to extract four characters beginning with the third character.
Is That All?
There is one more improvement left to talk about, involving the READE and REDPE operations. As you may already know, these operations have been extremely useful tools to read a database file forwards or backwards, looking for records that have the same key. But they did require you to code the key value (literal or field name) as Factor 1. With Release 3.0, this is no longer necessary; if you omit an entry in Factor 1, READE and REDPE will find the next or previous record having the same key as the last record read. It's that simple.
The Future
Beyond the points we have explored in this article, there are expectations and rumors aplenty. Sometime around 1992 or 1993, IBM is planning to revamp the C-specs in order to expand the Result Field to ten characters and add one more column to Decimal Positions, allowing you to define fields with ten or more decimal positions; the actual limit of nine is not ample enough.
There is also talk of allowing arithmetic expressions and a form of indention. However, it seems -- as far as IBM is concerned -- that RPG will remain basically columnar in nature.
Good News for RPG/400
Figure 1 Initialization subroutine
Figure 1: Initialization Subroutine ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C *INZSR BEGSR C TIME SYS 120 C MOVELSYS SYSTIM 60 C MOVE SYS SYSDAT 60 C ENDSR ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
Good News for RPG/400
Figure 10 The Substring operation
Figure 10: The Substring Operation ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C 4 SUBSTLONG:3 SHORT
Good News for RPG/400
Figure 2 Message description
Figure 2: Message Description ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C *INZSR BEGSR C CALL 'SUBR23R3' C PARM 'HDG0001' MSGID 7 C PARM MSGTXT132 C PARM 3 MSGLVL 10 C PARM RTNCDE 10 C PARM TXTLEN 40 C CALL 'SUBR23R3' C PARM 'HDG0002' MSGID 7 C (etc.) C ENDSR ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
Good News for RPG/400
Figure 3A Initializing a data structure globally
Figure 3a: Initializing a Data Structure Globally ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 ISMPLDS IDS I 1 10 ALPHA1 I 11 150NUM1 I 16 170NUM2 I 18 20 ALPHA2 ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
Good News for RPG/400
Figure 3B Initializing a data structure by subfields
Figure 3b: Initializing a Data Structure by Subfields ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 ISMPLDS DS I 1 10 ALPHA1 I I -70 11 150NUM1 I 16 170NUM2 I I 'AZ' 18 20 ALPHA2 ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
Good News for RPG/400
Figure 4 Uses of the CLEAR operation
Figure 4: Uses of the CLEAR Operation Type of Variable Equivalent To ---------------- ----------------- Alphanumeric MOVE *BLANK Numeric Z-ADD *ZERO Alpha array MOVEA *BLANK Numeric array MOVEA *ZERO Data structure MOVE *BLANK or Z-ADD *ZERO Indicator MOVE '0' or SETOF
Good News for RPG/400
Figure 5 *INZSR subroutine
Figure 5: *INZSR Subroutine ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C *INZSR BEGSR C MOVE 'CM' RCDTYP 2 C ENDSR ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
Good News for RPG/400
Figure 6 Concatenating two fields
Figure 6: Concatenating Two Fields ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C FIRST CAT LAST:1 NAME
Good News for RPG/400
Figure 7 Scanning a name in a file
Figure 7: Scanning a Name in a File ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C READ CUSTMAST 99 C *IN99 DOWEQ'0' C 'SMITH' SCAN CUNAME POSITN 20 C POSITN IFGT *ZERO C (what to do if found) C END C READ CUSTMAST 99 C END ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
Good News for RPG/400
Figure 8 Scanning with selection criteria
Figure 8: Scanning With Selection Criteria ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 C FIRST:4 SCAN NAME:2 POSITN
Good News for RPG/400
Figure 9 Scanning multiple occurrences
Figure 9: Scanning Multiple Occurrences ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7 E ARR 8 2 0 C 'T' SCAN NAME ARR ARR,1 1 (found in first position) ARR,2 6 (found in sixth position) ARR,3 7 (found in seventh position) ARR,4 10 (found in tenth position) ARR,5 11 (found in eleventh position) ARR,6 0 (no more occurrences of 'T') ARR,7 0 (same) ARR,8 0 (same) ....1.... ....2.... ....3.... ....4.... ....5.... ....6.... ....7
LATEST COMMENTS
MC Press Online