Everybody Needs an Alias
When creating new AS/400 database fields, go the extra step and add the ALIAS keyword, as well as the COLHDG and TEXT keywords. The ALIAS keyword will create an alternate field name.
When creating a program, the compiler determines if the ALIAS name is to be used. Most languages on the AS/400 will not take advantage of the ALIAS keyword. So why use it? The alternate field name (ALIAS) will be used when you access the file from Microsoft Excel or Access or from most PC-based, SQL-driven software.
After downloading a file, open it in Excel; instead of seeing a cryptic six-character field name, you see up to 30 characters of descriptive text. For example, if you use code like that in Figure 1, users could see OPERATOR_ON_HOURS instead of the cryptic name FATRON in the Excel column heading.
The DDS manual states that the length of an alternative field name is 1 to 30 characters. The first character must be A through Z. Subsequent characters must be A through Z, 0 through 9, or the underscore (_), with no blanks.
You must make sure alternative field names you specify conform to the naming conventions of the high-level language that uses the names. The high-level language compiler checks the syntax of the names when they are brought into the program. In other words, DDS does not perform any language-specific syntax checking. You probably will not have to worry about this; as I’ve stated, most AS/400 languages ignore this ALIAS keyword and use the regular field name.
—Jeff Kinzer ACH Food Companies
A FATRON 2 0 COLHDG('Operator' 'On Hours')
A TEXT('Operator On Hours')
Figure 1: Define aliases in keyword area of DDS specs.
Retaining Subfile Fold Status
Normally, when you use the SFLDROP keyword on a multiline subfile record, the subfile records come out truncated (that is, only the first line is displayed). The user can switch between truncated and multiline mode using a command key.
The SFLFOLD keyword works in the opposite way. If you press Enter and redisplay the screen, the subfile does not retain its current setting but reverts to its original setting.
By using the SFLMODE keyword, you can control the setting of the display and preserve it from one EXFMT to the next. The key is using an indicator to jointly control the SFLDROP and SFLFOLD keywords. Assuming you were to use indicator 16 (*IN16) as your controlling indicator, the following two lines would do the work for you:
A 16 SFLDROP(CF16)
A N16 SFLFOLD(CF16)
If *IN16 is off when the program begins, the display shows the entire subfile line. If *IN16 is on, the subfile has truncated lines. However, at this point there is no way to tell what state the subfile is in when the user exits. That’s done through the SFLMODE keyword with the following two lines:
A XHMODE 1 H
Now, when the EXFMT returns (due to a command key or Enter), a 0 in XHMODE indicates that the screen was fully extended, and a 1 indicates it was truncated. But, because you’re using *IN16 to control that on the EXFMT, you can move XHMODE into *IN16. In RPG, the code required to handle this looks like the following:
C MOVE XHMODE *IN16
With this code in place, the display retains its state (folded or truncated) from one EXFMT to the next. You can download the DDS and RPG III source code from which these lines were taken from the Midrange Computing Web site at www.midrangecomputing.com/mc.
%FOUND vs. %EOF
Here’s a “gotcha!” with the %FOUND and %EOF built-in functions that has bitten a couple of people in our shop. The program compiles correctly but produces incorrect results or loops. The original code, like that in Figure 2, used resulting indicators and worked fine. The problem came about when we tried to replace indicators with built-in functions, as in Figure 3. The problem is that CHAIN sets the %FOUND condition, but READE sets the %EOF condition.
For your future reference, the table in Figure 4 shows which I/O operations set %EOF and which set %FOUND. Are you surprised to see that WRITE can set %EOF? This case applies only to subfiles and means the subfile is full.
To fix the problem, we had to replace CHAIN with SETLL and READE, as shown in Figure 5. Notice that the file is specified on the %EOF function. Specifying the file name results in more bulletproof code for two reasons:
• If you use %FOUND or %EOF without specifying a file, you are checking a global condition that can be set by I/O operations on other files and by the LOOKUP, SCAN, CHECK, and CHECKR op codes. If you specify a file, you are checking a condition that is unique to that file.
• The code is easier to understand with the file name specified and doesn’t require significant effort to maintain.
c Key chain MYFILE 90
c dow not *in90
* some processing
c Key reade MYFILE 90
c enddo c Key chain MYFILE
c dow %found
* some processing
c Key reade MYFILE
c enddo C Key setll MYFILE
C Key reade MYFILE
C dow not %eof(MYFILE)
* some processing
C Key reade MYFILE
Figure 2: Indicator-laden code works fine.
Figure 3: Replacing resulting indicators with the %FOUND function introduces an error.
Figure 4: Use these I/O operations to set the % EOF and % FOUND functions.
Figure 5: This is the proper way to replace the resulting indicator with a BIF.
Moving and Restoring *SAVF from a PC Without TCP/IP
With all the fuss over TCP/IP these days, it’s easy to forget that there are many AS/400 users out there who do not have access to TCP/IP-connected devices. For these folks, many of the tips and techniques that some take for granted just aren’t workable, at least not in the normal ways.
Take, for example, the need to move AS/400 save files (*SAVF) from a PC to the AS/400 so that they can be restored. Using FTP, it’s a straightforward, simple process: Create an empty SAVF on the AS/400 in any library you desire; start an FTP session; change the mode to binary to avoid data corruption; and then FTP the file to the *SAVF you created.
Users of non-TCP/IP-connected devices (i.e., SNA devices using a product such as NetSoft/Router or another SNA-based service to connect with and perform file transfers) can’t use FTP to transfer a *SAVF. What can they do?
If you are using Client Access/400’s Data Transfer, or any one of the numerous SNA-based file transfer methods out there, to move files from your PC to the AS/400, here’s how you can also move and restore an AS/400 *SAVF from your PC:
1. Create an empty *SAVF on the AS/400 in any library you choose. Use the Create Save File (CRTSAVF) command to create the save file.
2. Create a directory in the AS/400 Integrated File System (AS/400 IFS) to temporarily store the *SAVF in after you copy it from the PC. Use the Create Directory (CRTDIR) command to create the directory.
3. Map a drive on the PC to the AS/400 IFS. To do this, right-click on the Network Neighborhood icon on your PC’s desktop and select the Map Network Drive option. Enter a path name in the form of MyAS400 MyIFSDirectory. (MyAS400 is the name of your AS/400 and MyIFSDirectory is the name of the directory, including any subdirectories you created in step 2.)
4. Using Windows Explorer, open the PC directory where the *SAVF is stored. Right- click on this file and select COPY.
5. Using Windows Explorer, open the mapped directory to your AS/400. Right-click on this directory and select PASTE. The AS/400 *SAVF will be copied to the AS/400 IFS, to the directory you created in step 2.
6. On the AS/400, copy the *SAVF from the AS/400 IFS to the *SAVF you created in step 1, using the Copy From Stream File (CPYFRMSTMF) command as follows:
Replace the library, directory, and save file names with your own names. You can now use either the standard Restore Library (RSTLIB) command or the Restore Object (RSTOBJ) command to restore the contents of the *SAVF.
—Shannon O’Donnell Senior Technical Editor
The JTOpen Experiment
IBM has two versions of the Java Toolbox for AS/400. Customers who want the “official” toolbox can use the Licensed Program Product (LPP) 5769JC1. For others, there exists an open-source version of the Java Toolbox. (I imagine some companies may have reservations about using an open-source product as part of their core, mission-critical business applications.)
The open-source version, JTOpen, is a superset of the latest toolbox LPP classes and bug fixes, plus additions from contributors outside of IBM. IBM fixes and enhancements to the core classes will appear in JTOpen first, since they can be made available immediately via the Web without waiting for a group PTF or OS/400 release.
Let me address some of the questions I have heard:
• Which is better, JTOpen or the LPP? Users who want the latest, greatest toolbox features should use JTOpen. Anything new added to the LPP will appear in JTOpen first; the same goes for fixes. Also, you can benefit from new classes contributed by your peers.
• Won’t open source mean a lot of junk makes it into the toolbox? No way! No additions can be made to the JTOpen product without the approval of the JTOpen core team. Right now, the core team includes three IBMers and two non-IBMers. Testing requirements and coding standards are rather strict. Fears that open source might lead to “junk” code are misplaced.
• Can I go to IBM Support Line for help with JTOpen? No. If you require that kind of support, stick with the LPP. JTOpen does, however, have a forum at http:// as400service.ibm.com/j_dir/JTOpen.nsf/($All)?OpenView, where you can post questions. Also, JTOpen supports a bug tracking and reporting Web site at http://oss.software. ibm.com/developerworks/opensource/jt400/bugs. Both of these sites are actively monitored and supported by IBM and others in the toolbox user community. So far, I think the support has been amazingly good—much better than what I have gotten used to from the normal IBM Support Line.
• Can I get source code? Yes. The availability of source code (for the access classes) is another big reason to go with JTOpen. Haven’t you ever wondered how some IBM software worked under the cover? Or maybe you thought you could make some improvements. Well, now you can!
• Where do I find out about JTOpen? Go to the JTOpen project Web site at http://oss.software.ibm.com/developerworks/opensource/jt400/.
• How can I tell what is coming out in future toolbox releases? Go to the JTOpen and AS/400 Toolbox for Java Work Items Website at http://oss.software.ibm.com/developerworks/opensource/jt400/todo.html, where you can see what is next on the horizon. You can also see what is on the books but has not yet been assigned. Have a neat idea? Submit it to one of the core team members.
This is a grand experiment for IBM. If we support IBM and really embrace this project, who knows what else might be opened up?
What’s Running on Which Port?
Having trouble connecting your TCP/IP-based service, such as a Java Data Queue application you wrote, to your AS/400?
The problem may be that the port that service has been assigned to on your AS/400 is not listening. Here’s how you can find out.
From an AS/400 command line, issue the Network Status (NETSTAT) command and select option 3, Work with TCP/IP connection status. This will display all active (listening) connections on your AS/400.
If the service isn’t active, use the Start Host Servers (STRHOSTSVR) command to start the service you are interested in. If, on the other hand, the service is already active and listening, the problem is likely with the TCP/IP service or application that’s trying to connect to your AS/400. It may be attempting to connect to the wrong port. In that case, use the guide shown in Figure 6 to make sure that your application is attempting to connect to the right port.
Senior Technical Editor
A Primer on Radix Conversions
Inevitably, a software developer must convert radices. Look at an IP address data structure through an AS/400 debugger, for example, and you might see hexadecimal nibbles in place of dotted decimals. Or look at file control definitions in source member QSYSINC/H/FCNTL, and prodigal son octal makes a cameo appearance. How do you make sense of these numbers in a human-friendly radix such as base ten? Simply punch them into a pocket calculator with a radix conversion button, of course. But if your pocket calculator is AWOL, the manual method I learned in school from M. Morris Mano’s book Digital Logic and Computer Design is an old friend that bears revisiting.
Consider 246185 in base (radix) decimal. To convert to hexadecimal, place your pencil on the right side of a sheet of paper. Divide 16 into 246185 with long division. The result is quotient 15386 and remainder 9. Move your pencil to the left and divide 16 into 15386, the prior quotient. The result is new quotient 961 and remainder 10. Continue to divide 16 into successive quotients. Work across the page from right to left. Stop when you get a zero quotient. Figure 7 shows that five steps are necessary to get a zero quotient. When you get a zero quotient, read the bottom row of remainders from left to right. As
Port Numbers for Host Servers and Server Mapper
Service Name Description Port Number
as-central Central server 8470 as-database Database server 8471 as-dtaq Data queue server 8472 as-file File server 8473 as-netprt Network print server 8474 as-rmtcmd Remote command/ Program call server 8475 as-signon Sign-on server 8476 as-svrmap Server mapper 449 drda Distributed Data Management (DDM) 446 as-usf Ultimedia facilities 8480 as-admin-http HTTP administration 2001 as-mtgctrl Management Central 5555 telnet Telnet server 23
Port Numbers for Host Servers and Daemons that Use Secure Sockets Layer (SSL)
Service Name Description Port Number
as-central-s Secure central server 9470 as-database-s Secure database server 9471 as-dtaq-s Secure data queue server 9472 as-file-s Secure file server 9473 as-netprt-s Secure network print server 9474 as-rmtcmd-s Secure remote command/ Program call server 9475 as-signon-s Secure sign-on server 9476 ddm-ssl Distributed Data Management (DDM) 448 as-usf-s Ultimedia facilities 9480 as-admin-https HTTP administration 2010 as-mgtctrl-ss Management Central 5566 Telnet-ssl Telnet server 992
Figure 6: Determine whether your application is attempting to connect to the right port.
shown in Figure 7, they are 3, 12, 1, 10, and 9. Replacing remainder values 10 to 15 with letters A to F gives 3C1A9. This is the answer. Decimal 246185 is equal to hexadecimal 3C1A9. That’s all there is to it!
To convert decimal to octal, use a constant divisor of 8. If you converted decimal 246185 to binary, your constant divisor would be 2, and your remainders would be a string of 1s and 0s.
To convert from a nondecimal radix to decimal, initialize an accumulator to zero. Read each source digit from left to right. Add each source digit to the accumulator, and if that source digit is not the last (right-most) digit, multiply the accumulator by the target radix number. Figure 8 shows an example of this. Hexadecimal 3C1A9 is being converted to decimal. The constant multiplier is 16. The final answer is decimal 246185.
To convert between two nondecimal numbers, first convert the source number to an intermediate number in a common radix form, and then convert the intermediate number to the target radix.
Figure 9 shows a REXX procedure, BAS001RX, which prompts for an input number, an input base, and an output base. Using arithmetic operators, BAS001RX converts the input number to an intermediate decimal number, converts the intermediate decimal number to the target number, and displays the result. You can download this utility from www.midrangecomputing.com/mc.
0 3 60 961 15386
16/3 16/60 16/961 16/15386 16/246185
3 12 1 10 9
3 (input hexadecimal 3)
+ 12 (input hexadecimal C)
+ 1 (input hexadecimal 1)
+ 10 (input hexadecimal A)
+ 9 (input hexadecimal 9)
/* PROGRAM - BAS001RX */
/* FUNCTION - convert base */
/* TO RUN - STRREXPRC SRCMBR(BAS001RX) SRCFILE(xxx/QREXSRC) */
Say 'Type any number.'; Pull Source; If Source = '' Then Return
Say 'What is the input base?'; Pull InBase; If InBase = '' Then Return
Say 'What is the output base?'; Pull OutBase; If OutBase = '' Then Return
Numeric Digits 50
Alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
Sum = 0
Do X = 1 To Length( Source )
Digit = Substr( Reverse( Source ), X, 1 )
Place = Pos( Digit, Substr( Alphabet, 1, InBase ))
If Place = 0 Then Do; Say Digit 'is not legal in Base' InBase; Exit; End
Sum = Sum + ( Place - 1 ) * InBase ** ( X - 1 ); End X
Target = ''
Do Until Sum = 0
Target = Substr( Alphabet, 1 + Sum // OutBase, 1 ) || Target
Sum = Sum % OutBase; End
Say 'Base' InBase 'input:' Source
Figure 7: These steps convert decimal 246185 to hexadecimal 3C1A9.
Figure 8: These steps convert hexadecimal 3C1A9 to decimal 246185.
Say 'Base' OutBase 'output:' Target
Figure 9: The REXX utility BAS001RX converts between any two radices.
Having Your Sort Both Ways
Q: I have defined an array to be in descending sequence, but I’d like to be able to sort it in ascending sequence. Does anyone have a way to do this without having to create a duplicate array?
— John M. Daley
A: You can use an overlay rather than a duplicate array, as illustrated in Figure 10.
—Barbara L. Morris
D array S 10a dim(35) descend
D arrayAscend S like(array) based(pArray)
D pArray S * inz(%addr(array))
* Sort array ascending
C sorta arrayAscend
Figure 10: Using an overlay allows for sorting an array in opposite sequence.
Make Called Programs Accept Either Packed or Zoned Decimal Parameters
I do a good bit of programming for shops that run S/36 applications on their AS/400s. One of the problems I run into frequently is that a S/36 RPG II program needs to call an RPG III program and pass it numeric parameters. For instance, consider an accounts-receivable
inquiry program that accepts a seven-digit packed decimal parameter that indicates customer number. The parameter list looks like this:
C *ENTRY PLIST
C PARM CUST 70
Calling it from other RPG III or RPG IV programs is no trouble, since those languages pass numeric parameters in packed decimal format. S/36 RPG II programs, however, pass numeric parameters in zoned decimal format. So making an RPG II program call the RPG III inquiry program is no simple matter.
I have developed several ways to handle this problem, but the one I prefer is making the called program accept either type of numeric value. RPG II programs can pass a zoned decimal number, while RPG III and RPG IV (and even CL, if needed) programs pass packed decimal numbers.
Figure 11 is a portion of an RPG III accounts-receivable inquiry program that can accept either type of parameter. It first determines whether or not the decimal number is zoned data. If the number is not zoned data, the program assumes that the number is packed. Of course, someone could pass in data that fits neither category and cause a data decimal error, but that is true of packed decimal parameters in general. When these calculations are finished, the CUST variable will contain the customer number.
—Ted Holt Senior Technical Editor
ICUSWRK DS I 7
I 1 70ZONCUS
I P 1 40PAKCUS
C *ENTRY PLIST
C PARM CUSIN 7
C MOVELCUSIN CUSWRK
C ‘ ‘:’0’ XLATECUSWRK CUSWRK
C TESTN CUSWRK 212121
C *IN21 IFEQ *ON
C Z-ADDZONCUS CUST 70
C Z-ADDPAKCUS CUST
Figure 11: A program is more flexible if it can accept either packed or zoned decimal data.