Close

Tips & Techniques: The IFS is More Than What You C
Written by: Bowen, Pamela
Category: Application Development
Date of Issue: 1999/05 1999/05

The IFS Is More Than What You C
_________________________________________________________________________________________________________________________________

The APIs used to access data in the IFS are commonly thought of as C language functions. However, RPG and COBOL programmers can also use these APIs to seamlessly access all types of data in all AS/400 file systems. This article will help you get started.

Part of base OS/400*, the IFS incorporates all AS/400 file systems and the data contained in those file systems, including byte stream files, into a hierarchical directory tree structure. Each file system has a set of logical structures and rules for interacting with information in storage. The structures and rules may differ between file systems, but because the file systems are integrated, the interfaces that are part of the IFS function identically, regardless of the file system being accessed.

IFS APIs can access almost any type of AS/400 data. In fact, some types of AS/400 data--including "root" (/) and QOpenSys files systems and all file systems and data located on the integrated PC server (IPCS)--are accessible only through IFS APIs. This makes IFS APIs valuable to programmers needing to access data in applications where the file system or object type is unknown. Remote data located on NFS, OS/2*, and Windows NT* servers that can be accessed through the AS/400 namespace can also be accessed via the IFS APIs.

One type of data that IFS APIs do not provide complete access to is DB2/400 (or database) files, which contain built-in structures made up of fields and records. IFS APIs can, however, provide access for text mode program-described physical files containing a single field and source physical files containing a single text field. In binary mode, they can access externally described physical files in addition to those files supported for text-mode access. Traditional APIs can also access database files.

ACCESSING IFS APIs
The IFS APIs are accessible from RPG, COBOL, and other integrated language environment (ILE) languages. They are part of a group of UNIX* APIs that make up the Single UNIX Specification. About 150 of the APIs contained in the Single UNIX Specification (previously called Spec 1170) are available on the AS/400 as a part of the IFS.

As of yet, IBM has not officially published ILE COBOL or RPG include files for IFS APIs. This means you must define your own versions of the C prototypes, constants, macros, and structures. To start, find the C header files for the items you need to use the selected API, and then code an RPG or COBOL version of them. These include files are an optionally loadable part of OS/400. This task is best handled by a C programmer. A copy of Kernighan and Ritchie's The C Programming Language is also helpful when interpreting these include files.

Finally, you need to explicitly bind to the service program (*SRVPGM) containing the IFS APIs. This step is necessary because there are no published mappings between APIs and the service programs that export them, and because the defaulting binding directories for COBOL and RPG programs may not include all service programs needed. The service program containing the IFS APIs is included in the binding directory QC2LE. Here's one way to create an RPG program that uses the IFS APIs:

CRTBNDRPG PGM(MYLIB/MYCODE) SRCFILE(MYLIB/QRPGLESRC) DFTACTGRP (*YES) BNDDIR(QC2LE)

THE IFS OPEN( ) API
Several basic APIs operate on a file. But before APIs can access a file, an API is needed to open it. The IFS open( ) API takes a path name to a file and, if successful, returns an integer called a file descriptor to the application. The application then passes this file descriptor to other APIs, such as read( ), write( ), and close( ), to perform operations on the file.

Several important pieces of information are passed to the open() API. First is the path name, expected to be a null-terminated string encoded in the coded character set identifier (CCSID) of the job. An example of a path name is /home/joe/jobfiles/ resume.txt. Next is an integer called the open flag (oflag). This is a combination of flags that indicate how to open the file. For example, O_RDONLY requests the file be opened for read access only, so the resulting descriptor cannot be used with the write( ) API. O_CREAT allows you to create a file if it does not exist. And O_TEXTDATA means the file is open for text (rather than binary) processing, and data conversion will be performed, if necessary. The constants for the oflags are described in the <fcntl.h> C header file located in QSYSINC/H.FCNTL. Be careful to interpret these values correctly--they are represented in octal in the header file. The oflag and other constants need to be defined explicitly in your RPG or COBOL program.

In the following example, the O_TEXTDATA flag is declared as the value 16777216 in decimal--the value corresponds to the octal value in the header file of 0100000000. You may choose to express the values in hexidecimal (for example O_TEXTDATA as x'0001000000') to make them more readable.

Following the oflag are a number of optional parameters. In C, this is indicated by the ellipses (...) in the prototype. In RPG, this is handled by using the keyword OPTIONS(*NOPASS) on the parameter definition in the D spec as shown in the following example. In COBOL prior to OS/400 V4R3, you need either a PTF to accommodate such a prototype or you need to code the parameters to always be present. Here is an example of open in RPG:

D Name S 50A INZ('/JUNK.DAT')
************************************
* OPEN( ) PROTOTYPE FROM <FCNTL.H> *
* WHICH IS FOUND IN QSYSINC/H.FCNTL*
************************************
D open PR 10I 0 EXTPROC('OPEN')
D * Value OPTIONS(*STRING)
D 10I 0 VALUE
D 10U 0 VALUE OPTIONS(*NOPASS)
D 10U 0 VALUE OPTIONS(*NOPASS)
...
******************************
* VALUES FOR "OFLAG" based on*
* <FCNTL.H> WHICH IS FOUND *
* IN QSYSINC/H.FCNTL AND *
* INDICATES OPEN SEMANTICS. *
******************************
DO-TEXTDATA S 10I 0 INZ(16777216)
DO-RDWR S 10I 0 INZ(4)
DOFLAG S 10I 0 INZ(0)
...
C Z-ADD O-RDWR OFLAG
C ADD O-TEXTDATA OFLAG
C EVAL FD=OPEN(NAME:
C OFLAG)
C IF FD = -1
C EVAL MYERROR(MSGP)


IFS READ( ), WROTE( ), and CLOSE( ) APIs
Compared to the open( ) API, the read( ), write( ), and close( ) APIs are simple. These APIs take a file descriptor (received from open( )) rather than a path name, avoiding the complications associated with the string. Obviously, to have a file descriptor, an open( ) must have been performed before any of these APIs can be used.

Besides the file descriptor, read( ) and write( ) only require you to pass a buffer and the size of the buffer. The close( ) API requires no additional parameters. A close( ) is required for each open( ); however, when you sign off or your job ends, all open files close automatically.

The prototypes for these APIs are located in the <unistd.h> C header file, found in QSYSINC/H.UNISTD. Remember that data written or read is automatically translated on each write if the file is opened with the O_TEXTDATA flag.

D rc S 10I 0
D fd S 10I 0
D msg S 50A INZ('write( )failed.')
D msgP S * INZ(%ADDR(msg))
D buf S 50A
D bufP S * INZ(%ADDR(buf))
D buflen S 10U 0 INZ(50)


*************************************
* write( ) prototype from <unistd.h> *
* which is found in QSYSINC/H.UNISTD*
*************************************
D write PR 10I 0 EXTPROC('write')

D 10I 0 VALUE
D * VALUE
D 10U 0 VALUE
******************************
* fd was set by a previous *
* successful call to the *
* open( ) API above. *
******************************
C EVAL buf = 'Hello World' + X'15'
C EVAL rc=write(fd:bufP:buflen)
C IF rc = -1
C EVAL MY ERROR (msgP)
C ENDIF
...
...
D rc S 10I 0
D fd S 10I 0
D msg S 50A INZ('close( )failed.')
D msgP S * INZ(%ADDR(msg))
*************************************
* close( ) prototype from <unistd.h> *
* which is found in QSYSINC/H.UNISTD*
*************************************
D close PR 10I 0 EXTPROC('close')
D 10I 0 VALUE
******************************
* fd was set by a previous *
* successful call to the *
* open( ) API above. *
******************************

C EVAL rc=close(fd)
C IF rc = -1
C EVAL MY ERROR (msgP)
C ENDIF
...

A LITTLE ABOUT DATA CONVERSION
IFS APIs are commonly used to convert data encoded for AS/400 (EBCDIC) into a PC encoding (ASCII). To do this, use the open( ) API specifying the O_TEXTDATA and O_CODEPAGE open flags (oflags). Specify in the fourth parameter (which is optional) the code page to which you want data converted on a read, or from which to convert incoming data on a write. Another common use of data conversion by applications is to convert file data into the job CCSID for presentation to the user. If the fourth parameter is not specified but O_TEXTDATA is on, the code page associated with the current job is calculated and used.

Note that the parameter given on open( ) is a code page, rather than a CCSID, which is familiar to AS/400 programmers. This means some conversions--those for CCSIDs that are identified by more than one code page (Chinese, Japanese, and Korean)--are not yet supported ON IFS APIS. Many CCSIDs have the same number as their associated code pages (for example, English EBCDIC is CCSID 37 and also code page 37).

There is a special consideration for the open( ) API. Because you can provide only one code page on the open( ) API, if you want to create a file using the open( ) API and request data conversion from EBCDIC to ASCII, you actually need to open( ) the file twice. First, open( ) the file to create it and specify the EBCDIC code page. Then close( ) the file and reopen it, this time specifying the ASCII code page for conversions between EBCDIC and ASCII. Specifying the ASCII page ensures that each ASCII character your program writes to the file is converted by the system to its EBCDIC equivalent. This is much simpler than performing the work yourself.

SUMMARY OF CHALLENGES
If you aren't a C programmer, using IFS APIs pose a number of challenges. But these challenges can be overcome by following the simple steps summarized here:

1. Carefully read the API documentation. IFS APIs are documented in the book, System API Reference OS/400 Unix-Type APIs (SC41-5875-01). This information is also available on the Web at AS400BKS.rochester.ibm.com/cgi-bin/bookmgr/bookmgr.cmd/BOOKS/QB3AM401/CCONTENTS. However, the IFS APIs in this reference are currently only documented for C usage.
2. Locate the C header file name at the front of each API section.
3. Check the table in the back of the System API Reference to find the LIBRARY/FILE.MEMBER where each of the C header files are located.
4. Go to the specific C header files on the AS/400 and find the constants, structures, and macros needed to create COBOL or RPG versions in your program.
5. Be careful with constants in C. They could be in octal (0100), hexadecimal (0x40), or decimal (64). In this example, these values are identical.

OTHER REFERENCES

  • Integrated File System Introduction (SC41-4711) is a source of basic information.
  • AS/400 National Language Support (SC41-5101).
  • Code snippets Web page--www.as400.ibm.com/snippets. Choose "Search," then search on "Programming Language" (RPG or COBOL source).
    The names of the snippets are: "USING IFS FROM RPG" and "USING IFS FROM COBOL."
  • For help with code samples, contact Ray Bills at IBM. His e-mail address is ray@us.ibm.com.

    -- PAMELA BOWEN is a software engineer in the AS/400 Integrated File System and Servers area of IBM Rochester. She can be reached at pbowen@us.ibm.com.