|
|||||||
|
|
![]() |
|
|
Reading an IFS File from RPG by Shannon O'Donnell [The code for this article is available for download.] Building your RPG programming skills means that sometimes you have to do something other than code yet another 5250 subfile application. Sometimes you have to go beyond your comfort level and spend some time learning how to interact with other OS/400 objects. For example, reading files stored in the OS/400 Integrated File System. In this article, we'll take a look at how you can read OS/400 IFS files from within an RPG IV program. Why Read IFS Files? Why would you ever need to read the data from a non-DB2/400 file? There are lots of possibilities. For example, if you are writing RPG IV CGI applications to push data to the users' Web browser, you might want to store HTML source code in the IFS. In that case, you might want to read those HTML files into your CGI program at runtime so you could then push them to the Web. Another use might be if you have data stored in a simple text, RTF, or other type of file, and you want to get that data into your AS/400 application. Using a little imagination, I'm sure you can think of a lot of great reasons for storing data in the IFS and then reading it into an RPG program. The Unix-Type APIs Since there is not an OS/400 command that you can use to read through a non-DB2/400 data file, we'll have to do things the hard way. Well, not terribly hard, but at the very least different. We'll need to read a file from the IFS to use Unix-type APIs. Specifically, we will need to use the open(), read(), and close() APIs. The open() API will allow us to open an IFS file by path and file name. The read() API allows us to read the contents of the IFS file, and the close() API allows us to close that file. Pretty simple, huh? In fact, as you'll see, the Unix-type APIs we are going to use in our example are actually pretty easy to use, once you get over your fear of APIs. As with any API used in an RPG IV program, the first thing we need to do is to define the API's prototype in the D-specs. The open() API needs to know the name of the file (including the full path to that file), as well as what permissions you want to open it with (such as open for READ_ONLY or open for READ_UPDATE), what "mode" you want to open the file in (such as user authorities), and what code page you want to open the file with. The open() API will return a handle to the opened file; it will return an error if the file was not found or if it is unable to be opened using the permission, mode, or code page you requested. Code the open() API as shown here:
D open pr 10i 0 ExtProc('open')
D filename * value
D openflags 10i 0 value
D mode 10u 0 value options(*nopass)
D codepage 10u 0 value options(*nopass)
The read() API needs the handle to the IFS file, retrieved by the open() API; the address to the variable that the data read from the file will be stored in; and, finally, the number of bytes of data to read at one time. The read() API will return an error if the read was unsuccessful. Code the read() API as shown here:
D read pr 10i 0 ExtProc('read')
D filehandle 10i 0 value
D datareceived * value
D nbytes 10u 0 value
The close() API only needs the handle to the file retrieved by the open() API. Code the close() API as shown here:
D close pr 10i 0 ExtProc('close')
D filehandle 10i 0 value
The permissions and mode parameters are controlled by numeric values. For convenience, I've defined some of the more common ones you are likely to need:
* values for oflag parameter, used by open()
D O_APPEND s 10i 0 inz(256)
D O_CODEPAGE s 10i 0 inz(8388608)
D O_CREAT s 10i 0 inz(8)
D O_EXCL s 10i 0 inz(16)
D O_RDONLY s 10i 0 inz(1)
D O_RDWR s 10i 0 inz(4)
D O_TEXTDATA s 10i 0 inz(16777216)
D O_TRUNC s 10i 0 inz(64)
D O_WRONLY s 10i 0 inz(2)
* = = = = = = = = = = = = = = = = = = = = = = = =
* user authorities for
* omode parameter, used by open()
* from QSYSINC/SYS, member STAT
D S_IRUSR s 10i 0 inz(256)
D S_IWUSR s 10i 0 inz(128)
D S_IXUSR s 10i 0 inz( 64)
D S_IRWXU s 10i 0 inz(448)
* group authorities
D S_IRGRP s 10i 0 inz( 32)
D S_IWGRP s 10i 0 inz( 16)
D S_IXGRP s 10i 0 inz( 8)
D S_IRWXG s 10i 0 inz( 56)
* other authorities
D S_IROTH s 10i 0 inz( 4)
D S_IWOTH s 10i 0 inz( 2)
D S_IXOTH s 10i 0 inz( 1)
D S_IRWXO s 10i 0 inz( 7)
The Sample Program The sample program, which you can download with this article, does nothing more than display an empty 5250 subfile and prompt the user for the name, including the full path, of an IFS file. When the user enters that name in the prompt field, the RPG program will attempt to open and read the IFS file indicated and to display that data in the subfile. Keep in mind that if you are reading a non-text file, say a JPG or a BMP image file, the data displayed may be unintelligible. In that case, you may want to first translate the data from ASCII to EBCDIC using the QDCXLATE API. Even then, all you'll end up with is a series of numeric values for non-text files. However, in this manner you can store the raw data of a non-text file in a DB2 database, if your application requires it.
The main routine in this sample program is the RdIFSFil subprocedure. This is the code that will interact with the IFS. The RdIFSFil subprocedure needs to have a prototype defined for it in the main D-specs of the program before you can use it. However, it's a simple prototype. Merely pass it a 40-character alpha field (the same length as the input field on the display prompt), which contains the path and name of the IFS file. When the subprocedure is executed, it will first attempt to open the file, using the open() API and the parameters (permissions, mode, codepage) you passed to it. If you need a combination of permissions, simply concatenate them together, as shown in the code sample you downloaded, using the "+" operator. If the file open is successful, processing will continue. Otherwise, the error flag will be set and the logic will return to the statement that called this subprocedure. Reading the file, in this example, involves iterating through a loop and calling the GetChar subroutine. The GetChar subroutine will read() 256 characters at a time from the IFS file (you can change this amount) into the field named Data_Rec. Data_Rec is then parsed, one character at a time, into the variable named CurChar. This is done so that if you should ever need to test for individual characters, such as line feeds or carriage returns, you can do so by checking the contents of CurChar. Another way to handle this would have been to simply read 78 characters at a time (the length of our subfile field) and then write that to the subfile. I chose to do it this way so that I can easily expand the functionality of my program in the future, should I need to do so. The program will iterate through the data 78 times (the length of the subfile field) and then write that data to the subfile. When the file has been completely read, the subfile will be written one last time, to dump any remaining data to the screen, and then the close() API will be called to close the IFS file. The rest of the program contains routine programming techniques that you've seen a thousand times, so I will leave it to you to study it on your own. Power to the API The Unix-type APIs are a powerful set of tools you can use to expand your knowledge of RPG, OS/400, and manipulating the Integrated File System. For full details on the various parameters for the Unix-type APIs explained in this article, and for a list of all of the Unix-type APIs, go to the IBM iSeries Information Center.
|
Editors
Contact the Editors |
| Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved. |