Yes, %EOF(), %FOUND, and %ERROR work in "fixed format." Back when i5/OS was still called OS/400 (Version 4, Release 2 to be exact), IBM introduced several built-in functions that eliminated the need to use any Resulting Indicators on all but twp opcodes. Those orphaned opcodes were LOOKUP and TESTN.
Note: The best RPG IV training you can find is going on this May in Las Vegas. Join Bob Cozzi, George Farr, Aaron Bartell, and others for RPG World [2]. And use promotion code RPGCODER to save $100 off registration.
These new (well, new at the time) built-in functions eliminated the final reason to use indicators at all in RPG IV, except for *INLR (and IBM deprecated LR in i5/OS Version 6). Now when you CHAIN to a database file, rather then checking if the NOT FOUND indicator was set ON, you simply check if the record was %FOUND. Pretty cool, huh?
Now that you know these built-in functions were introduced in OS/400 Version 4, Release 2, there should be a realization . . . they work in fixed format in the Extended Factor 2.
C custno Chain CustRec
C if %found()
C** I found the record!
C else
C** Darn it!
C endif
I was surprised recently when I learned that a large number of RPG programmers (those who have entered the market post-V4R2, were unaware that these built-in function actually work in fixed format. They thought they worked only in free-format syntax.
The above code can be migrated to free format as follows:
C/free
chain custNo CustRec;
if %found();
// We found the record!
else;
// Darn it!
endif;
/end-free
These built-in functions were created because free format was coming in Version 5 of OS/400, so IBM needed a way to allow most of the opcodes to work without the use of Resulting Indicators. These built-in functions were the solution, and a good one.
So what did IBM do with the TESTN (test for numeric data) and LOOKUP opcodes? The TESTN is gone. It doesn't exist in free format. But the LOOKUP opcode got some new life. It was replaced by the following 10 built-in functions:
The %LOOKUPxx built-in functions work with arrays, whereas %TLOOKUPxx work with tables. (Does anyone still write new code with tables?)
This may be why some programmers overlooked %LOOKUP for free format; they tried %LOOKUPEQ and got a compile error. Yes the "EQ" version of %LOOKUP and %TLOOKUP do not have the "EQ" suffix, unlike the other variations. But other than that one subtle difference, they syntax is identical for each.
For %LOOKUP:
%LOOKUPxx(searchValue : array {: startingElem {: numberOfElems}})
Where "xx" is blank for equal, LT for less than, LE for less than or equal, GT for great than, and GE for greater than or equal.
For %TLOOKUP:
%TLOOKUPxx(searchValue : searchedTable {: altTable})
Where "xx" is blank for equal, LT for less than, LE for less than or equal, GT for great than, and GE for greater than or equal.
I'll forget about %TLOOKUP for now and focus on %LOOKUP for a moment. One powerful new feature over its opcode version is that you can specify the starting array element and number of elements to search. This means you don't have to use the %SUBARR (subscript array) built-in function to search a portion of an array, and that's pretty cool.
One thing that needs to be pointed out is that the %LOOKUP built-in function performs a different routine to search the array than does the legacy LOOKUP opcode. So there is a chance you could get a different result if you blindly convert LOOKUP opcodes to %LOOKUP built-in functions.
LOOKUP generated a subroutine based on the Resulting Indicators and performed a sort of linear scan to locate matching elements, whereas %LOOKUP uses a binary search routine to find the elements. This means that if the data is not sequenced, %LOOKUP could fail to locate the elements even if they exist.
If your shop is running i5/OS Version 5 Release 3, then you also have the cool %SUBARR built-in function. This function allows you to use any array in your program as if it were a smaller size. This is great for situations where you have a large array for year-end runs, and weekly or monthly runs require a smaller number of array elements.
Using %SUBARR you can subscript the array, using only those elements you want. For example, the SORTA (sort array) opcode is usable when you have an array of 200+ elements of which you want to sort only the first 35. To do that you would code something like this:
D myStuff S 7P 2 Dim(250)
D elems S 5I 0
/free
// pseudo code: fill the array with 35 elements
// Now sort only the first 35 elements
sorta %subarr( myStuff : 1 : 35);
/end-free
A lot of programmers don't seem to remember that there are actually three versions of the %TRIM built-in function.
%TRIM is like a fire axe -- break glass in case of emergency. In other words, Don't use it unless it is required. Using %TRIM(myVar) strips off both trailing and leading blanks, when 90 percent of the time you probably only want trailing blanks removed, so %TRIMR(myVar) would be good enough.
Why do you care? Because you're wasting CPU cycles/time doing something that Isn't necessary. %TRIM is slower than %TRIML or %TRIMR, so use the right one for the job and nobody gets hurt.
I must get this question, oh let me think . . . everyday. In traditional RPG syntax, if you want to move a character value into a numeric field, you would code something like the following:
D myChar S 5A Inz('3741')
D myDigits S 5P 0
C move myChar MyDigits
The problem with this technique is that the data in MYCHAR is left-justified, so the result of this MOVE operation is that MYDIGITS = 37410, not 3741 as expected.
You can fix that using built-in functions with either the EVAL opcode and the Extended Factor 2 or free format, as follows:
D myChar S 5A Inz('3741')
D myDigits S 5P 0
C eval myDigits = %dec(myChar:5:0)
/free
myDigits = %dec(myChar:5:0);
/end-free
Both versions correctly produce a value for MYDIGITS of 3741.
And if the data in MYCHAR was right-justified (a leading blank preceded the 3741), the results of using %DEC would be the same, 3741.
Converting character to numeric is actually more accurate using the %DEC built-in function, just be sure to include the length and decimal positions as the second and third parameters respectively; they have to be hard-coded. I'm unclear as to why IBM requires them to be hard-coded, as the underlying CVTEFN MI instruction doesn't require hard-coding. You can fake it out, however, by doing the following:
D myChar S 5A Inz('3741')
D myDigits S 5P 0
D theLen C Const(%len(myDigits))
D theDecPos C Const(%decpos(myDigits))
/free
myDigits = %dec(myChar:theLen:theDecPos);
/end-free
Links:
[1] http://systeminetwork.com/author/bob-cozzi
[2] http://www.rpgworld.com/