Chapter 4. Accessing stream files randomly

Table of Contents
4.1. Positioning to a given point in the file
4.2. Example of using lseek() to jump around the file
4.3. Organizing a stream file into records
4.4. Calculating number of records in a file
4.5. Example of reading/writing/updating records in a stream file

In chapter 2, we wrote data to the file at the start, and then read it from the start of the file. We always read the file sequentially. In this chapter, I'll show you how to read the file randomly.

4.1. Positioning to a given point in the file

In a standard physical file (one without key fields) you can position by record number using the SETLL and SETGT operations. This makes it possible to access a file "randomly" (or, in other words, you don't have to read through the file sequentially, you can "jump around".)

For a stream file, to read the file randomly, you use the "lseek()" API. However, since stream files are not organized into records by themselves, the lseek() API doesn't seek to a record number. Instead, you give it an "offset" which indicates a number of bytes, rather than records, to jump to.

Here is the C-language prototype for the lseek() API, followed by the corresponding RPG prototype:

      off_t lseek(int fildes, off_t offset, int whence)                   

     (1)D lseek           PR            10I 0 ExtProc('lseek')
     (2)D   fildes                      10I 0 value 
     (3)D   offset                      10I 0 value 
     (4)D   whence                      10I 0 value 
The return value will be the new offset from the beginning of the file if lseek() was successful, otherwise it will be -1, and errno will be set.
This is just the file descriptor of the stream file that you've opened.
Here you put the offset. The offset is the number of bytes to move forward in the file from the point that's specified in the next parameter. If you wish to move backward instead of forward, you can specify a negative number. You can also specify zero if you want to move to exactly the point given in the next parameter.
The "whence" parameter specifies where you want to move to, or start counting your offset from. It can be one of three named constants:
  • SEEK_SET means that the offset will be from the beginning of the file. An offset of 0 would be the very first byte in the file.

  • SEEK_CUR means that the offset will be from the current position in the file. For example, if you wanted to re-read the last 5 bytes, you could code SEEK_CUR with an offset of -5

  • SEEK_END means that the offset will be from the end of the file.

Now that I've told you about the named constants, it'd probably be a good idea to add them to our /copy member, eh?

     D SEEK_SET        C                   CONST(0)       
     D SEEK_CUR        C                   CONST(1)       
     D SEEK_END        C                   CONST(2)       

Here's a sample of jumping to the end of the file in RPG:

     c                   if        lseek(fd: 0: SEEK_END) < 0    
     c                   callp     EscErrno(errno)                      
     c                   endif                                   

How about jumping to the 157th byte from the start of the file?

     c                   if        lseek(fd: 157: SEEK_SET) < 0    
     c                   callp     EscErrno(errno)                      
     c                   endif                                   

Or, if we're already at byte 157, we could easily seek forward to byte 167, like this:

     c                   if        lseek(fd: 10: SEEK_CUR) < 0    
     c                   callp     EscErrno(errno)                      
     c                   endif