Reading IFS Files in RPG

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Using RPG's traditional file input/output (I/O) operation codes to read files from the Integrated File System (IFS) is virtually impossible. The only way to do that today is to write a "driver" program that is used in conjunction with the SPECIAL device file. It would be beneficial to have integrated support for accessing all database systems and all file systems from within RPG, but we don't. So we have to make our own!

Fortunately, we can easily write the interfaces needed to access the IFS within RPG. Actually, although very little programming is required, there is a good bit of research required. But in the end, it works pretty well, so it's worth it.

It may seem as though the ongoing theme in several issues of Midrange Developer has been calling C language functions from within RPG IV. What is really happening is that IBM has exposed a number of APIs though the usual Qxxxxxx naming convention and also provided the C language with a callable function to do the same task (wrapping the API's complexities in some cases). Since the C language requires all functions to be callable via what's known as a "procedure pointer," IBM has had to externalize these C language functions. Therefore, it is often easier to call with the C language interface than the actual raw API itself. Remember, until Java came along, all the non-AS/400 programmers in the world were telling us how great C was and that it was the only language you need to use...so why not steal their functions and use them in good old RPG!

There are APIs, for example, that allow you to allocate memory dynamically while a program is running. In the C language, those APIs are wrapped by the C language runtime malloc(), realloc(), and free() functions. Until RPG IV recently added ALLOC, REALLOC, and DEALLOC to the language as native operation codes, you had a choice: call the OS/400 memory allocation APIs or call the much simpler C language functions.

The IFS is no different. The C language has a couple of options for accessing files stored in the IFS. You can open a file and read it in blocks--that is, read a specified number of bytes at a type. This is called byte I/O. You can also open an IFS file as a stream file, which is a different set of APIs, and use stream I/O to access the file. Stream I/O provides for record, or more accurately, line-at-a-time file access. You can read a line of a text file and write a line of a text file using stream I/O. If you use byte I/O, it is up to you to look at each input byte and decide what to do with it.

Normal IFS I/O (or byte I/O) has its advantages in that you can read both binary files, such as image files, and text files, such as HTML. Let's look at the APIs that support byte I/O first.

There are many file-oriented functions in the C language, but the I/O operations for the IFS include, but are not limited to, the following:

  • fclose()--Close file
  • feof()--Test for end-of-file indicator
  • ferror()--Test for read/write errors
  • fgetpos()--Get file position
  • fgets()--Read a string
  • fopen()--Open file
  • fputs()--Write string
  • fread()--Read a number of bytes from an IFS file
  • fseek()--Reposition file cursor
  • fsetpos()--Set file cursor
  • fwrite()--Write bytes to IFS file

These functions, like all C language functions, are case-sensitive. They are all lowercase names. They will not work if called as anything other than fully lowercase names.

Note that each function begins with the letter "f." This stands, appropriately enough, for "file." If you look at the C compiler, however, you'll find that the native versions of these interfaces do not access the IFS. In fact, the C language compiler directive IFCTRL(*IFSIO) needs to be specified in order to access the IFS natively. So an attempt to call these interfaces directly from RPG IV will fail.

As it turns out, the compiler actually uses a different set of APIs to access the IFS file system when IFCTRL(*IFSIO) is specified. Fortunately, since the compiler needs to change the actual names of the procedures being called, the C source code is provided. A quick review of the C "header files" (as they're called) reveals that all the interfaces are translated to API names that are similar to the original names with the addition of a "_C_IFS_" prefix. That is, they are named _C_IFS_xxxxx, where xxxxx is the original procedure name. So fopen() is translated to _C_IFS_fopen, again a case-sensitive name--uppercase for "_C_IFS_" and lowercase for "fopen." These are the procedure names we can call to get to the IFS from within RPG IV.

So, under the new naming convention, the proper IFS API names needed to access the IFS include the following:

  • _C_IFS_fclose()--Close file
  • _C_IFS_feof()--Test for end-of-file indicator
  • _C_IFS_ferror()--Test for read/write errors
  • _C_IFS_fgetpos()--Get file position
  • _C_IFS_fgets()--Read a string
  • _C_IFS_fopen()--Open file
  • _C_IFS_fputs()--Write string
  • _C_IFS_fread()--Read a number of bytes from an IFS file
  • _C_IFS_fseek()--Reposition file cursor
  • _C_IFS_fsetpos()--Set file cursor
  • _C_IFS_fwrite()--Write bytes to IFS file
  • _C_IFS_fdopen()--Associates a file handle with an IFS file
  • _C_IFS_fflush()--Flush out the write buffer to the file
  • _C_IFS_fgetc()--Read a character
  • _C_IFS_fileno()--Determine file handle associated with the IFS file
  • _C_IFS_fprintf()--Write formatted data to a file
  • _C_IFS_fputc()--Write a character
  • _C_IFS_freopen()--File redirector
  • _C_IFS_fscanf()--Read formatted data
  • _C_IFS_ftell()--Get current file cursor position

For a complete list of the IFS APIs, see the source file member named IFS in the source file named H in library QSYSINC: SRCFILE(QSYSINC/H) MBR(IFS)

I've selected a few of the IFS APIs for review here. These allow basic file open, close, read, write, delete end-of-file, and file positioning operations. Remember, IFS files are not DB2/400 database files, so the meaning of update and delete are different. With IFS files, there is no update operation--just a write operation that overwrites existing bytes in the file.

Let's look at a few of the IFS APIs and create prototypes so they may be called directly from within RPG IV.

FILE* _C_IFS_fopen(const char* filename, const char* mode);

The first parameter is the name of the file in the IFS that is to be open. The second parameter specifies the open options for the file, such as read/write/modify.

In RPG IV, use the VALUE keyword for these parameters rather than use the CONST keyword. VALUE provides results similar to CONST, but it allows you to avoid using the %ADDR built-in function.

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++++++
     D ifsOpen         PR              *   ExtProc('_C_IFS_fopen')
     D  fileName                       *   Value Options(*STRING)
     D  filemode                       *   Value Options(*STRING)

Figure 1: The ifsOpen prototype

The first parameter is the name of the file to be open, but since it is a C null terminated string, it will require the file name to have a hex x (X'00') immediately after the last non-blank character in the file name. So passing a literal name, such as '/www/index.html' is perfectly valid, but passing the name of the file within a 50-position field would require the addition of the %TRIMR operation. For example, the following are valid IFSopen statements:

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++++++
0001 D welcome         S            255a   INZ('/www/index.html')
0002 D pFile1          S               *   INZ
.....CSRn01..............OpCode(ex)Extended-factor2+++++++++++++++++++
0003 C                   Eval      pFile1=fOpen(%TrimR(welcome) :'r'
0004 C                   Eval      pFile1=fOpen('/www/index.html':'r')

Figure 2: Use of ifsOpen with a field and a literal

Line 1 declares a 255-byte character field that contains the name of the file (including its directory) on the IFS. Line 3 opens that file. Note the use of %TRIMR to eliminate the trailing blanks from the name. It is important use %TRIMR; otherwise, the file name will not be found. RPG's OPTIONS(*STRING) causes the value being passed to be converted into a null-terminated string. It does this by passing the value to the called function as a 256-position value and inserting X'00' in that extra position. So OPTIONS(*STRING) increases the passed length by one byte and fills that one extra byte with a null value.

Line 2 declares a pointer variable. When the IFS APIs open a file, a pointer to the file is returned. That file pointer is used by (i.e., passed to) all other IFS APIs that access the same file. So the pointer variable is similar to the file name on a regular File Description specification.

Line 4 in Figure 2 uses a quoted literal instead of a field name to open the IFS file. Note that since a quoted character string is being used, no %TRIMR is required.

To open a file on the IFS for read-only, use the RPG prototype IFSopen with the name of the file as the first parameter and set the mode option (second parameter) to 'r'. Figure 3 contains a list of the available mode options and combinations. Note that all modes must be specified in lowercase.

Open Mode
Description
r
Open a text file for reading. The file must exist.
w
Create a text file for writing. If the file exists, its contents are destroyed.
a
Open a text file for writing at the end of the file. If the file doesn't exist, it is created. Does not destroy file contents, but also does not move the end-of-file marker. Use 'a+' for most append operations.
r+
Open a text file for reading and writing. The file must exist.
w+
Open a text file for reading and writing. If the file exists, its contents are destroyed. That is, the file is cleared.
a+
Open a text file for reading and appending; the appending operation includes the removal of the EOF marker before new data is written to the file, and the EOF marker is repositioned after writing is complete. If the file does not exist, it is created.
rb
Open a binary file for reading. The file must exist.
wb
Create an empty binary file for writing. If the file exists, its contents are cleared. That is, the file is virtually erased and recreated.
ab
Open a binary file in append mode for writing at the end of the file. If the file doesn't exist, it is created.
r+b or rb+
Open a binary file for reading and writing. The file must exist.
w+b or wb+
Create an empty binary file for reading and writing. If the file exists, its contents are cleared. That is, the file is virtually erased and recreated.
a+b or ab+
Open a binary file in append mode for writing at the end of the file. If the file doesn't exist, it is created.

Figure 3: ifsOpen mode settings

As indicated in Figure 3, use a lowercase "r" to open files for read-only. Use "a+" to open files for reading and writing (input/output).

Once an IFS file is open, it can be read from or written to (depending on the mode options). As the file is being written to, the file's position is adjusted to point to the end-of-file so that you are always writing to the end of the IFS file. Use the ifsSeek API to position the file to any byte position in the file.

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++++++
0001 D ifsReadLine     PR              *   ExtProc('_C_IFS_fgets')
0002 D  inBuffer                       *   Value Options(*STRING)
0003 D  inBufLen                     10I 0 Value
0004 D  stream_FILE                    *   Value

Figure 4: ifsReadLine--Read a "record" from an IFS text file

Line 1 in Figure 4 prototypes the IFS fgets() procedure. This procedure reads data from the IFS file up to a carriage return/line feed sequence, which effectively reads a line or record from an IFS text file. Using this method--as opposed to using some of the other APIs that are available--makes it much easier to read text-based IFS files.

You can easily prototype the other IFS procedures so that they can be called from RPG IV.

The IFS is becoming more and more important to the AS/400 programmer. Saving and retrieving both formatted and unformatted data, as well as reading data sent to your iSeries from non-iSeries systems, means IFS file access is here to stay.

BOB COZZI

Bob Cozzi is a programmer/consultant, writer/author, and software developer. His popular RPG xTools add-on subprocedure library for RPG IV is fast becoming a standard with RPG developers. His book The Modern RPG Language has been the most widely used RPG programming book for more than a decade. He, along with others, speaks at and produces the highly popular RPG World conference for RPG programmers.


MC Press books written by Robert Cozzi available now on the MC Press Bookstore.

RPG TnT RPG TnT
Get this jam-packed resource of quick, easy-to-implement RPG tips!
List Price $65.00

Now On Sale

The Modern RPG IV Language The Modern RPG IV Language
Cozzi on everything RPG! What more could you want?
List Price $99.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  •  

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: